Hay dos formas de añadir nuevas funciones a MySQL:
-
Puede añadir funciones con la interfaz de funciones definidas de usuario (UDF) (N.del T. Acrónimo para User Defined Functions). Las funciones definidas por el usuario se ocmpilan como ficheros objeto y se añaden y borran del servidor dinámicamente usando los comandos
CREATE FUNCTION
yDROP FUNCTION
. Consulte Sección 27.2.2, “Sintaxis deCREATE FUNCTION/DROP FUNCTION
”. -
Puede añadir funciones como funciones nativas MySQL. Se compilan en el servidor mysqld y están disponibles permanentemente.
Cada método tiene ventajas y desventajas:
-
Si escribe funciones definidas por el usuario, debe instalar ficheros objeto además del servidor mismo. Si compilar su función en el servidor, no necesita hacerlo.
-
Puede añadir UDFs a distribuciones binarias MySQL . Las funciones nativas requieren modificar una distribución fuente.
-
Si actualiza su distribución MySQL , puede continuar usando las UDFs previamente instaladas, a no ser que actualice a una versión en la que la interfaz UDF cambie. (Un cambio incompatible ocurrió en MySQL 4.1.1 para funciones agregadas. Una función llamada
xxx_clear()
debe definirse en lugar dexxx_reset()
.) Para funciones nativas, debe repetir sus modificaciones cada vez que actualice.
Use el método que use para añadir nuevas fucnciones, pueden
invocarse en comandos SQL como funciones nativas tales como
ABS()
o SOUNDEX()
.
Otra forma de añadir funciones es creando funciones almacenadas.
Se escriben con comandos SQL en lugar de compilando código
objeto. La sintaxis para escribr funciones almacenadas se describe
en Stored Procedures
.
La siguiente sección describe características de la interfaz UDF, proporciona intrucciones para escribir UDFs, y discute sobre precauciones de seguridad que toma MySQSL para prevenir un mal uso de UDF.
Para código fuente de ejemplo que ilustra cómo escribir UDFs,
mire el fichero sql/udf_example.cc
que se
proporciona en las distribuciones fuentes de MySQL.
La interfaz de MySQL para funciones definidas por el usuario proporciona las siguientes funcionalidades y capacidades:
-
Las funciones pueden retornar cadenas de carácteres, enteros o valores reales.
-
Puede definir funciones simples que operen en con un único registro a la vez, o agregar funciones que operen con grupos de registros.
-
Se proporciona información a las funciones que permite chequear el tipo y número de argumentos que se les pasa.
-
Le puede decir a MySQL que coercione argumentos de un tipo dado antes de pasarlos a la función.
-
Puede indicar que una funcion retorne
NULL
o que ha ocurrido un error.
CREATE [AGGREGATE] FUNCTIONfunction_name
RETURNS {STRING|INTEGER|REAL} SONAMEshared_library_name
DROP FUNCTIONfunction_name
Una funciones definidas por el usuario (UDF) es un modo de
extender MySQL con una nueva función que funciona como una
función nativa de MySQL tal como ABS()
o
CONCAT()
.
function_name
es el nombre que debe
usarse en comandos SQL para invocar la función. La cláusula
RETURNS
indica el tipo del valor de retorno
de la función. shared_library_name
es el nombre base de la fichero del objeto compartido que
contiene el código que implementa la función. El fichero debe
localizarse en un directorio en el que busque el lincador
dinámico del sistema.
Para crear una función , debe tener el privilegio
INSERT
para la base de datos
mysql
. Para borrar una función, debe tener
el privilegio DELETE
para la base de datos
mysql
. Esto es así porque CREATE
FUNCTION
añade un registro a la tabla de sistema
mysql.func
que registra los nombres de
función, tipo, y nombre de la biblioteca compartida, y
DROP FUNCTION
borra el registro de la
función de dicha tabla. Si no tiene esta tabla, debe ejecutar
el script mysql_fix_privilege_tables para
crearla. Consulte Sección 2.10.2, “Aumentar la versión de las tablas de privilegios”.
Una función activa es una que se ha cargado con CREATE
FUNCTION
y no se ha eliminado con DROP
FUNCTION
. Todas las funciones activas se recargan cada
vez que el servidor arranca, a no ser que arranque
mysqld con la opción
--skip-grant-tables
. En este caso, la
inicialización de UDF no se hace y no están disponibles.
Para instrucciones sobre escribir funciones definidas por el usuario consulte Sección 27.2.3, “Añadir una nueva función definida por el usuario”. Para que funcione el mecanismo de UDF, las funciones deben escribirse en C o C++, su sistema operativo debe soportar carga dinámica y debe haber compilado mysqld dinámicamente (no estáticamente).
AGGREGATE
es una nueva opción para MySQL
3.23. Una función AGGREGATE
funciona
exactamente como una función agregada (resumen) de MySQL tal
como SUM
o COUNT()
. Para
que funcione AGGREGATE
, su tabla
mysql.func
debe contener una columna
type
. Si su tabla
mysql.func
no tiene esta columna, debe
ejecutar el script mysql_fix_privilege_tables
para crearla.
Para que funciones el mecanismo UDF, las funciones deben
escribirse en C o C++ y su sistema operativo debe soportar carga
dinámica. La distribución fuente de MySQL incluye un fichero
sql/udf_example.cc
que define 5 nuevas
funciones. Consulte este fichero para ver cómo funcionan las
convenciones de llamadas de UDF.
Para poder usar UDFs, necesita lincar mysqld
dinámicamente. No configure MySQL usando
--with-mysqld-ldflags=-all-static
. Si quiere
usar una UDF que necesite acceder a símbolos desde
mysqld (por ejemplo la función
metaphone
en
sql/udf_example.cc
que usa
default_charset_info
), debe lincar el
programa con -rdynamic
(consulte man
dlopen
). Si planea usar UDFs, la rula es configurar
MySQL con --with-mysqld-ldflags=-rdynamic
a no
ser que tenga una muy buena razón para no hacerlo.
Si usa una distribución precompilada de MySQL, use MySQL-Max, que contiene un servidor lincado dinámicamente que soporta carga dinámica.
Para cada función que quiera usar en comandos SQL, debe definir
las funciones correspondientes en C (o C++). En la siguiente
discusión, el nombre “xxx” se usa como nombre de
función de ejemplo. Para distinguir entre el uso de SQL y C/C++
, XXX()
(mayúsculas) indicata una llamada de
función SQL, xxx()
(minúsculas) indica una
llamada de función C/C++ .
Las funciones C/C++ que escribe para implementar la interficie
para XXX()
son:
-
xxx()
(requerido)La función principal. Es donde el resultado de la función se computa. La correspondencia entre los tipos de datos de la función SQL y el tipo de retorno de la función C/C++ se muestra aquí:
SQL Type C/C++ Type STRING
char *
INTEGER
long long
REAL
double
-
xxx_init()
(opcional)La función de inicialización para
xxx()
. Puede usarse para:-
Chequea el número de argumentos para
XXX()
. -
Chequea que los argumentos son de un tipo requerido o, alternativamente, le dice a MySQL que coercione argumentos a los tipos que quiera cuando se llama a la función principal.
-
Reserva cualquier memoria requerida por la función principal.
-
Especifica la longitud máxima del resultado.
-
Especifica (para funciones
REAL
) el máximo número de decimales. -
Especifica si el resultado puede ser
NULL
.
-
-
xxx_deinit()
(opcional)La función de deinicialización para
xxx()
. Debe liberar cualquier memoria reservada por la función de inicialización.
Cuando un comando SQL invoca XXX()
, MySQL
llama a la función de inicialización
xxx_init()
para que realice cualquier
inicialización necesaria, tales como chequeo de argumentos o
reserva de memoria. Si xxx_init()
retorna un
error, el comando SQL se aborta con un mensaje de error y no se
llama ni a la función principal ni a la de deinicialización.
En caso contrario, se llama a la función principal
xxx()
una vez para cada registro. Tras
procesar todos los registros, se llama a la función de
deinicialización xxx_deinit()
para que pueda
realizar cualquier limpieza requerida.
Para funciones agregadas que funcionan como
SUM()
, debe proporcionar las siguientes
funciones:
-
xxx_reset()
(necesaria antes de 4.1.1)Resetea el valor agregado actual e inserta el argumento como valor agregado inicial para un nuevo grupo.
-
xxx_clear()
(requerido a partir de 4.1.1)Resetea el valor agregado actual pero no inserta el argumento como valor agregado inicial para un nuevo grupo.
-
xxx_add()
(requerido)Añade el argumento al valor agregado actual.
MySQL trata UDFs agregados como sigue:
-
Llama a
xxx_init()
para permitir a la función agregada reservar la memoria necesaria para ordenar resultados. -
Ordena la table según la expresión
GROUP BY
. -
Llama a
xxx_clear()
para el primer registro en cada grupo. -
Llama a
xxx_add()
para cada nuevo registro que permita al mismo grupo. -
Llama a
xxx()
para obtener el resultado del agregado cuando el grupo cambia o cuando el último registro se ha procesado. -
Repite 3-5 hasta que se procesan todos los registros
-
Llama a
xxx_deinit()
para permitir al UDF liberar la memoria reservada.
Todas las funciones deben ser flujos seguros. Esto incluye no
sólo la función principal,también las funciones de
inicialización o deinicialización, y las funciones adicionales
requeridas por las funciones agregadas. Una consecuencia de esta
restricción es que no se le permite reservar ninguna variable
global o estática que cambien! Si necesita memoria, debe
reservarla en xxx_init()
y liberarla en
xxx_deinit()
.
Esta sección describe las distintas funciones que necesita definir cuando crea un UDF simple. Sección 27.2.3, “Añadir una nueva función definida por el usuario” describe el orden en que MySQL llama a estas funciones.
La función principal xxx()
debe declararse
como se muestra en esta sección. Tenga en cuenta que el tipo
de retorno y los parámetros difieren, dependiendo de si
declara la función SQL XXX()
para retornar
STRING
, INTEGER
, o
REAL
en el comando CREATE
FUNCTION
:
Para funciones STRING
:
char *xxx(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error);
Para funciones INTEGER
:
long long xxx(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
Para funciones REAL
:
double xxx(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
Las funciones de inicialización y deinicialización se declaran así:
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void xxx_deinit(UDF_INIT *initid);
El parámetro initid
se pasa a las tres
funciones. Apunta a la estructura UDF_INIT
que se usa para comunicar información entre funciones. Los
mienbros de la estructura UDF_INIT
se
muestran a continuación. La función de inicialización debe
rellenar cualquier miembro que quiera cambiar. (Para usar el
valor por defecto de un miembro no lo cambie.)
-
my_bool maybe_null
xxx_init()
debe asignar amaybe_null
1
sixxx()
puede retornarNULL
. El valor por defecto es1
si alguno de los argumentos se declaranmaybe_null
. -
unsigned int decimals
El número de decimales. El valor por defecto es el número máximo de decimales en los argumentos pasados a la función principal. (Por ejemplo, si a la función se pasa
1.34
,1.345
, y1.3
, el valor por defecto es 3, ya que1.345
tiene 3 decimales. -
unsigned int max_length
Longitud máxima del resultado. El valor por defecto
max_length
difiere en función del tipo de resultado de la función. Para funciones de cadenas de carácteres, el valor por defecto es el argumento más largo. Para funciones enteras, el valor por defecto es de 21 dígitos. Para funciones reales, el valor por defecto es 13 mas el número de decimales indicados porinitid->decimals
. (Para funciones numéricas, la longitud incluye cualquier signo o carácter de punto decimal.)Si quiere retornar un valor blob, puede asignar a
max_length
de 65KB a 16MB. Esta memoria no se reserva, pero el valor se usa para decidir qué tipo de columna usar si hay una necesidad de almacenar los datos temporalmente. -
char *ptr
Puntero que la función puede usar para su propio propósito. Por ejemplo, las funciones pueden usar
initid->ptr
para comunicar memoria reservada entre ellos.xxx_init()
debe reservar la memoria y asignarla al puntero:initid->ptr = allocated_memory;
En
xxx()
yxxx_deinit()
, refiérase ainitid->ptr
para usar o liberar la memoria.
Esta sección describe las distintas funciones que necesita definir cuando crea un UDF agregado. Sección 27.2.3, “Añadir una nueva función definida por el usuario” describe el orden en que MySQL llama a estas funciones.
-
xxx_reset()
Esta función se llama cuando MySQL encuentra el primer registro en un nuevo grupo. Debe resetear cualquier variable resumen interna y usar el argumento dado
UDF_ARGS
como primer valor en su resumen interno del grupo. Declarexxx_reset()
como se muestra:char *xxx_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
xxx_reset()
se necesita sólo antes de MySQL 4.1.1. NO se necesita usar desde MySQL 4.1.1, cuando la interfaz UDF cambió para usarxxx_clear()
en su lugar. Sin embargo, puede definirxxx_reset()
yxxx_clear()
si quiere que su UDF funcione antes y después del cambio de interfaz. (Si no incluye ambas funciones, la funciónxxx_reset()
en muchos casos puede implementarse internamente llamandoxxx_clear()
para resetear todas las variables, y luego llamarxxx_add()
para añadir el argumentoUDF_ARGS
como primer valor del grupo.) -
xxx_clear()
Esta función se llama cuando MySQL necesita resetear los resultados resumen. Se llama al principio para cada nuevo grupo pero sólo puede llamarse para resetear los valores para una consulta donde no hubieran registros que coincidan con la búsqueda. Declare
xxx_clear()
como sigue:char *xxx_clear(UDF_INIT *initid, char *is_null, char *error);
is_null
se asigna para que apunte aCHAR(0)
antes de llamar axxx_clear()
.Si algo falla, puede almacenar un valor en la variable a la que apunta el argumento
error
.error
apunta a una variable de un byte, no a un búfer de cadenas de carácteres.xxx_clear()
se requiere sólo a partir de MySQL 4.1.1. Antes de MySQL 4.1.1, usexxx_reset()
en su lugar. -
xxx_add()
Esta función se llama para todos los registros que pertenezcan al mismo grupo, excepto para el primer registor. Debe ussarlo para añadir el valor en el argumento
UDF_ARGS
a su variable de resumen interna.char *xxx_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error);
La función xxx()
para un UDF agregado debe
declararse de la misma forma que UDF no agregados. Consulte
Sección 27.2.3.1, “Secuencias de llamada UDF para funciones simples”.
Para un UDF agregado, MySQL llama a la función
xxx()
una vez que todos los registros en el
grupo han sido procesados. Normalmente no debe acceder el
argumento UDF_ARGS
aquí sino devolver un
valor basado en sus variables de resumen internas.
El tratamiento de valores retornados en
xxx()
debe hacerse del mismo modo que para
UDF no agregados. Consulte
Sección 27.2.3.4, “Valores de retorno y tratamiento de errores”.
Las funciones xxx_reset()
y
xxx_add()
tratan sus argumentos
UDF_ARGS
del mismo modo que las funciones
para UDFs no agregados. Consulte
Sección 27.2.3.3, “Proceso de argumentos”.
Los argumentos punteros de is_null
y
error
son los mismos para todas las
llamadas a xxx_reset()
,
xxx_clear()
, xxx_add()
y
xxx()
. Puede usar esto para recordar que
obtuvo un error o si la función xxx()
debería retornar NULL
. No debe almacenar
una cadena de carácteres en *error
!
error
apunta a una variable de un byte, no
a un búfer de cadenas de carácteres.
*is_null
se resetea para cada grupo (antes
de llamar xxx_clear()
).
*error
nunca se resetea.
Si *is_null
o *error
se
asignan cuando xxx()
retorna, MySQL retorna
NULL
como resultado para la función de
grupo.
El parámetro args
apunta a una estructura
UDF_ARGS
que tiene los miembros listados a
continuación:
-
unsigned int arg_count
Número de argumentos. Chequee este valor en la función de inicialización si necesita que su función sea llamada con un número particular de argumentos. Por ejemplo:
if (args->arg_count != 2) { strcpy(message,"XXX() requires two arguments"); return 1; }
-
enum Item_result *arg_type
Puntero a una matriz conteniendo los tipos para cada argumento. Los tipos posibles son
STRING_RESULT
,INT_RESULT
, yREAL_RESULT
.Par asegurar que los argumentos sean de un tipo dado y retorne un error si no lo son, chequee la matriz
arg_type
en la función de inicialización. Por ejemplo:if (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != INT_RESULT) { strcpy(message,"XXX() requires a string and an integer"); return 1; }
Como alternativa a requerir que los argumentos de la función sean de un tipo particular, puede usar la función de inicialización para asignar los elementos
arg_type
con los tipos que quiera. Esto hace que MySQL fuerce a los argumentos a los tipos para cada llamada dexxx()
. Por ejemplo, para especificar que los primeros dos argumentos se fuercen a una cadena de carácteres de enteros, respectivamente, haga lo siguiente enxxx_init()
:args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
-
char **args
args->args
comunica información a la función de inicialización acerca de la naturaleza general de los argumentos pasados a la función. Para un argumento constantei
,args->args[i]
apunta al valor del argumento. (Consulte a continuación instrucciones sobre cómo acceder al valor apropiadamente.) Para argumentos no constantes,args->args[i]
es0
. Un argumento constante es una expresión que usa sólo constantes, como3
o4*7-2
oSIN(3.14)
. Un argumento no constante es una expresión que se refiere a valores que pueden cambiar de registro a registro, tales como nombres de columna o funciones que se llaman con argumentos no constantes.Para cada invocación de la función principal,
args->args
contiene los argumentos que se pasan para el registro que está procesando.Las funciones pueden referirse a un argumento
i
como se muestra:-
Un argumento de tipo
STRING_RESULT
se da como puntero a una cadena de carácteres más una longitud, para permitir tratar datos binarios o datos arbitráriamente largos. Los contenidos de la cadena de carácteres están disponibles comoargs->args[i]
y la longitud de la cadena esargs->lengths[i]
. No debe asumir que las cadenas de carácteres están terminadas por null. -
Para un argumento de tipo
INT_RESULT
, debe convertirargs->args[i]
a un valorlong long
:long long int_val; int_val = *((long long*) args->args[i]);
-
Para un argumento de tipo
REAL_RESULT
, debe convertirargs->args[i]
a un valordouble
:double real_val; real_val = *((double*) args->args[i]);
-
-
unsigned long *lengths
Para la función de inicialización, la matriz
lengths
indica la longitud máxima de cadena de carácteres para cada argumento. No debe cambiar este valor. Para cada invocación de la función principallengths
contiene las longitudes reales de cualquier argumento de cadenas de carácteres que se pasa al registro en proceso. Para argumentos de tiposINT_RESULT
oREAL_RESULT
,lengths
contiene la longitud máxima del argumento (como para la función de inicialización).
La función de inicialización debe retornar
0
si no hay errores y 1
en cualquier otro caso. Si ocurre un error,
xxx_init()
debe almacenar un mensaje de
error terminado en null en el parámetro
message
. El mensaje se retorna al cliente.
El búffer de mensajes es de longitud
MYSQL_ERRMSG_SIZE
, pero debe tratar que el
mensaje ea inferior a 80 carácteres para que coincida con la
anchura de una pantalla de terminal estándar.
El valor retornado por una función principal
xxx()
es el valor de la función, para
funciones long long
y
double
. Una función de cadenas de
carácteres debe retornar un puntero al resultado y asignar
*result
y *length
con
los contenidos y longitud del valor de retorno. Por ejemplo:
memcpy(result, "result string", 13); *length = 13;
El búffer result
que se pasa a la función
xxx()
tiene longitud de 255 bytes. Si su
resultado coincide con esto, no tiene que preocuparse acerca
de reservar memoria para los resultados.
Si su función de cadenas de carácteres necesita retornar una
cadena de carácteres mayor a 255 bytes, debe reservar el
espacio para ello con malloc()
en su
función xxx_init()
o su función
xxx()
y liberarla en su función
xxx_deinit()
. Puede almacenar la memoria
reservada en la entrada ptr
en la
estructura UDF_INIT
para reusar para
llamadas futuras xxx()
. Consulte
Sección 27.2.3.1, “Secuencias de llamada UDF para funciones simples”.
Para indicar un valor de retorno de NULL
en
la función principal, asigne a *is_null
1
:
*is_null = 1;
Para indicar un retorno de error en la función principal,
inicialice *error
con 1
:
*error = 1;
Si xxx()
asigna a *error
1
para cualquier registro, el valor de la
función es NULL
para el registro actual y
para cualquier subsecuente registro procesado por el comando
en que se invoca XXX()
.
(xxx()
no se llama ni por registros
subsecuentes.) Nota: Antes de
MySQL 3.22.10, debe asignar tanto *error
como *is_null
:
*error = 1; *is_null = 1;
Los ficheros implementando UDFs deben compilarse e instalarse
en el equipo donde corre el servidor. Este proceso se describe
a continuación para el fichero UDF de ejemplo
sql/udf_example.cc
que se incluye en la
distribución fuente de MySQL.
Las instrucciones siguientes son para Unix. Las instructiones para Windows se dan posteriormente en esta sección.
El fichero udf_example.cc
contiene las
siguientes funciones:
-
metaphon()
retorna una cadena de carácteres metaphon del argumento de cadena de carácteres. Esto es algo como una cadena de carácteres soundex, pero más ajustado al inglés. -
myfunc_double()
retorna la suma de los valores ASCII de los carácteres en los argumentos, divididos por la suma de la longitud de los argumentos. -
myfunc_int()
retorna la suma de la longitud de los argumentos. -
sequence([const int])
retorna una secuencia empezando por el número dado o 1 si no se da ningún número. -
lookup()
retorna la IP para un nombre de equipo. -
reverse_lookup()
retorna el nombre de equipo para una IP. La función puede llamarse con un único argumento de cadena de carácteres de la forma'xxx.xxx.xxx.xxx'
o con cuatro números.
Un fichero cargable dinámicamente debe compilarse como fichero objeto compartible, usando un comando como:
shell> gcc -shared -o udf_example.so udf_example.cc
Si usa gcc, debe ser capaz de crear
udf_example.so
con un comando más
simple:
shell> make udf_example.so
Puede determinar fácilmente las opciones de compilación
adecuadas para su sistema ejecutando este comando en el
directorio sql
del árbol fuente de
MySQL:
shell> make udf_example.o
Debe ejecutar un comando de compilación similar al que
muestra make , excepto que debe quitar la
opción -c
cerca del final de la línea y
añadir -o udf_example.so
al final de la
línea. (En algunos sistemas, puede necesitar dejar
-c
en el comando.)
Tras compilar un objeto compartido conteniendo UDFs, debe
instalarlo y comunicarlo a MySQL. Compilar un objeto
compartido de udf_example.cc
produce un
fichero llamado algo como udf_example.so
(el nombre exacto puede variar de plataforma a plataforma).
Copie este fichero en un directorio como
/usr/lib
en el que busque el lincador
dinámico del sistema (en tiempo de ejecución), o añada el
directorio en el que está el objeto compartido en el fichero
de configuración del lincador (por ejemplo,
/etc/ld.so.conf
).
El nombre del lincador depende del sistema (por ejemplo, ld-elf.so.1 en FreeBSD, ld.so en Linux, o dyld en Mac OS X). Consulte su documentación del sistema para información acerca del nombre del lincador y cómo configurarlo.
En muchos sistemas, puede cambiar las variables de entorno
LD_LIBRARY
o
LD_LIBRARY_PATH
para que apunten al
directorio donde tiene los ficheros de su UDF. La página de
manual dlopen
explica qué variables debe
usar en su sistema. Debe inicializarla en los scripts de
arranque mysql.server o
mysqld_safe y reiniciar
mysqld.
En algunos sistemas, el programa ldconfig
que configura el lincador dinámico no reconoce un objeto
compartido a no ser que su nombre comience con
lib
. En ese caso debe renombar el fichero
como udf_example.so
a
libudf_example.so
.
En Windows, puede compilar funciones definidas por el usuario usando el siguiente procedimiento:
-
Necesita obtener el repositorio fuente BitKeeper para MySQL 4.0 o superior. Consulte Sección 2.8.3, “Instalar desde el árbol de código fuente de desarrollo”.
-
En el repositorio fuente, busque el directorio
VC++Files/examples/udf_example
. Hay ficheros llamadosudf_example.def
,udf_example.dsp
, yudf_example.dsw
. -
En el repositorio fuente, busque en el directorio
sql
. Copieudf_example.cc
de este directorio al directorioVC++Files/examples/udf_example
y renombre el fichero audf_example.cpp
. -
Abra el fichero
udf_example.dsw
con Visual Studio VC++ y úselo para compilar los UDFs como un proyecto normal.
Cuando el objeto compartido se ha instalado, notifique a mysqld con las nuevas funciones con estos comandos:
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME 'udf_example.so'; mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME 'udf_example.so'; mysql> CREATE FUNCTION lookup RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE FUNCTION reverse_lookup -> RETURNS STRING SONAME 'udf_example.so'; mysql> CREATE AGGREGATE FUNCTION avgcost -> RETURNS REAL SONAME 'udf_example.so';
Las funciones pueden borrarse con DROP
FUNCTION
:
mysql> DROP FUNCTION metaphon; mysql> DROP FUNCTION myfunc_double; mysql> DROP FUNCTION myfunc_int; mysql> DROP FUNCTION lookup; mysql> DROP FUNCTION reverse_lookup; mysql> DROP FUNCTION avgcost;
Los comandos CREATE FUNCTION
y
DROP FUNCTION
actualizan la tabla de
sistema func
en la base de datos
mysql
. El nombre de función, tipo y
nombre de biblioteca compartida se salvan en la tabla. Debe
tener los privilegios INSERT
y
DELETE
para la base de datos
mysql
para crear y borrar funciones.
No debe usar CREATE FUNCTION
para añadir
una función que se ha creado préviamente. Si necesita
reinstalar una función, debe borrarla con DROP
FUNCTION
y reinstalarla con CREATE
FUNCTION
. Puede necesitar hacer esto, por ejemplo si
recompila una nueva versión de su función, de forma que
mysqld tenga la nueva versión. De otro
modo, el servidor continua usando la versión antigua.
Una función activa es una que se ha cargado con
CREATE FUNCTION
y no se ha borrado con
DROP FUNCTION
. Todas las funciones activas
se recargan cada vez que el servidor arranca, a no ser que
arranque mysqld con la opción
--skip-grant-tables
. En ese caso, la
inicialización de UDFs no se hace y no están disponibles.
MySQL toma las siguientes medidas para evitar uso inadecuado de funciones definidas por el usuario.
Debe tener el privilegio INSERT
para poder
usar CREATE FUNCTION
y el privilegio
DELETE
para poder usar DROP
FUNCTION
. Esto es necesario ya que estos comandos
añaden y borran registros de la tabla
mysql.func
.
UDFs debe tener como mínimo un símbolo definido además del
símbolo xxx
que corresponde a la función
principal xxx()
. Estos símbolos
auxiliares se corresponden con las funciones
xxx_init()
,
xxx_deinit()
,
xxx_reset()
,
xxx_clear()
, y xxx_add()
. Desde MySQL 4.0.24, 4.1.10a, y 5.0.3,
mysqld soporta una opción
--allow-suspicious-udfs
que controla si UDFs
que tienen un solo símbolo xxx
pueden
cargarse. Por defecto, la opción está desactivada, para
evitar intentos de cargar funciones de ficheros de objetos
compartidos otros aparte de los que contienen UDFs legítimos.
Si tiene UDFs antiguos que contienen sólo el símbolo
xxx
y no pueden recompilarse para incluir
un símbolo auxiliar, puede ser necesario especificar la
opción --allow-suspicious-udfs
. De otro
modo, debe evitar activar esta capacidad.
Los ficheros objeto UDF no pueden guardarse en un directorio
arbitrario. Deben estar localizado en un directorio de sistema
en el que busque el lincador dinámico. Para forzar esta
restricción y evitar intentos de especificar rutas fuera de
los directorios buscados por el lincador dinámico, MySQL
chequea el fichero de objeto compartido especificado en
comandos CREATE FUNCTION
para delimitadores
de rutas. Desde MySQL 4.0.24, 4.1.10a, y 5.0.3, MySQL también
chequea los delimitadores de rutas en nombres de ficheros
almacenados en la tabla mysql.func
cuando
carga funciones. Esto evita intentos de especificar rutas
ilegítimas manipuladas en la tabla
mysql.func
. Para información acerca de
UDFs y lincador en tiempo de ejecución, consulte
Sección 27.2.3.5, “Compilar e instalar funciones definidas por el usuario”.
El procedimiento para añadir una nueva función nativa se describe aquí. Tenga en cuenta que no puede añadir funciones nativas a una distribución binaria ya que el procedimiento implica modificar código fuente MySQL. Debe compilar MySQL de una distribución fuente. También tenga en cuenta que si migra a otra versión de MySQL (por ejemplo, cuando una nueva versión aparece), necesita repetir el procedimiento con la nueva versión.
Para añadir una nueva función MySQL nativa, siga estos pasos:
-
Añada una línea en
lex.h
que defina el nombre de función en la matrizsql_functions[]
. -
Si el prototipo de función es simple (sólo tiene cero, uno, dos o tres argumentos), debe especificar en
lex.h
SYM(FUNC_ARG
N
) (dondeN
es el número de argumentos) como el segundo argumento en la matrizsql_functions[]
y añadir una nueva función que cree un objeto función enitem_create.cc
. Consulte"ABS"
ycreate_funcs_abs()
para un ejemplo.Si la función prototipo es complicada (por ejemplo, tiene un número variable de argumentos), debe añadir dos líneas en
sql_yacc.yy
. Una indica el símbolo de preprocesador que yacc debe definir (debe añadirse al principio del fichero). Luego defina los parámetros de función y añada un “item” con estos parámetros a la regla de parseosimple_expr
. Para un ejemplo, consulte todas las ocurrencias deATAN
ensql_yacc.yy
para ver cómo se hace. -
En
item_func.h
, declare una clase heredando deItem_num_func
oItem_str_func
, en función de si su función retorna un número o una cadena de carácteres. -
En
item_func.cc
, añada una de las siguientes declaraciones, dependiendo de si está definiendo una función numérica o de cadena de carácteres:double Item_func_newname::val() longlong Item_func_newname::val_int() String *Item_func_newname::Str(String *str)
Si hereda su objeto de cualquiera de los objetos estándar (como
Item_num_func
), probablemente sólo tiene que definier una de estas funciones y dejar que el objeto padre se ocupe de las otras funciones. Por ejemplo, la claseItem_str_func
define una funciónval()
que ejecutaatof()
en el valor retornado por::str()
. -
Debería probablemente definir la siguiente función objeto:
void Item_func_newname::fix_length_and_dec()
Esta función debe calcular al menos
max_length
basándose en los argumentos dados.max_length
es el máximo número de carácteres que la función puede retornar. Esta función debería también asignarmaybe_null = 0
si la función principal no puede retornar un valorNULL
. La función puede chequear si algunos de los argumentos de la función puede retornarNULL
chequeando la variablemaybe_null
de los argumentos. Puede consultarItem_func_mod::fix_length_and_dec
para un ejemplo típico de cómo hacer esto.
Todas las funciones deben ser flujos seguros. En otras palabras, no usar ninguna variable global o estática en las funciones sin protegerlas con semáforos
Si quiere retornar NULL
, desde
::val()
, ::val_int()
or
::str()
debe asignar a
null_value
1 y retornar 0.
Para funciones objeto ::str()
, hay algunas
consideraciones adicionales a tener en cuenta:
-
El argumento
String *str
proporciona un búffer de cadenas de carácteres que puede usarse para guardar el resultado. (Para más información acerca del tipoString
, consulte el ficherosql_string.h
.) -
La función
::str()
debe retornar la cadena de carácteres que tiene el resultado o(char*) 0
si el resultado esNULL
. -
Todas las funciones de cadenas de carácteres tratan de evitar cualquier reserva de memoria a no ser que sea absolutamente necesario!