Tabla de contenidos
Este capítulo describe muchas cosas que necesita saber cuando
trabaje con el código de MySQL. Si planea contribuir al
desarrollo de MySQL, quiere tener acceso al límite de código de
versiones, o sólo quiere estar al día del desarrollo, siga las
instrucciones en Sección 2.8.3, “Instalar desde el árbol de código fuente de desarrollo”. Si
está interesado en conceptos internos de MySQL, debe suscribirse
a la lista de correo internals
. Esta lista
tiene poco tráfico. Para más detalles sobre cómo suscribirse,
consulte Sección 1.6.1.1, “Las listas de correo de MySQL”. Todos los desarrolladores
en MySQL AB están en la lista internals
y
ayudamos a otra gente trabajando en el código de MySQL. Siéntase
libre de usar esta lista para preguntar cosas sobre el código y
para enviar parches con los que quiera contribuir al proyecto
MySQL!
MySQL server crea los siguientes flujos:
El flujo de conexión TCP/IP trata todas las peticiones de conexión y crea un nuevo flujo dedicado para tratar el proceso de autenticación y de consulta SQL para cada conexión.
En Windows NT hay un flujo para tratar los named pipes que hace el mismo trabajo que el flujo de conexiones TCP/IP en peticiones de conexión por named pipe.
El flujo de señales trata todas las señales. Este flujo
también trata normalmente alarmas y llamadas
process_alarm()
para forzar tiempos
máximos en las conexiones que llevan en espera demasiado
tiempo.
Si se compila mysqld con
-DUSE_ALARM_THREAD
,se crea un flujo
dedicado que trata alarmas. Sólo se usa en algunos sistemas
donde hay problemas con sigwait()
o si
quiere usar el código thr_alarm()
en su
aplicación sin un flujo dedicado a tratar señales.
Si usa la opción --flush_time=#
, se crea
un flujo dedicado para volcar todas las tablas en un
intervalo dado.
Cada conexión tiene su propio flujo.
Cada tabla diferente en que se usa INSERT
DELAYED
tiene su propio flujo.
Si usa --master-host
, se arranca un flujo
de replicación esclavo para leer y aplicar actualizaciones
del maestro.
mysqladmin processlist sólo muestra la
conexión, INSERT DELAYED
, y flujos de
replicación.
El sistema de testeo incluído en distribuciones fuente Unix y distribuciones binarias hace que sea posible para los usuarios y desarrolladores realizar tests de regresión en código MySQL. Estos tests pueden ejecutarse en Unix o en Windows (usando el entorno Cygwin) si el servidor se ha compilado bajo Cygwin. No pueden ejecutarse en un entorno Windows nativo.
El conjunto actual de casos de uso no testea todo en MySQL, pero debería atrapar la mayoría de bugs óbvios en el código de proceso SQL, temas de bibliotecas del SO, y es bastante útil para testear replicación. Nuestra meta eventual es que el test cubra el 100% del código. Los contribuidores de nuestra suite de test son bienvenidos. Puede querer contribuir con tests que examinen la funcionalidad crítica del sistema, ya que esto asegura que todas las futuras versiones de MySQL funcionen bien con sus aplicaciones.
El sistema de test consiste en un intérprete de idioma de
test (mysqltest), un shell script para
ejecutar todos los tests(mysql-test-run),
los casos de usos de test actuales escritos en un lenguaje
especial de test, y sus resultados esperados. Para ejecutar la
test suite en su sistema tras construirlo escriba
make test o
mysql-test/mysql-test-run
de la raíz del
código fuente. Si ha instalado una distribución binaria,
cd
a la raíz de la instalación (ej.
/usr/local/mysql
), y ejecute
scripts/mysql-test-run
. Todos los test
deberían tener éxito. Si no es así, debería intentar
encontrar porqué y reportar el problema si es un bug de
MySQL. Consulte Sección 27.1.2.3, “Reporting Bugs in the MySQL Test Suite”.
Desde MySQL 4.1 , si tiene una copia de
mysqld ejecutándose en la máquina donde
quiere ejecutar el test suite no tiene que pararlo, mientras
no use los puertos 9306
y
9307
. Si uno de esos puertos está ocupado,
debe editar mysql-test-run y cambiar los
valores de los puertos maestro y/o esclavo a uno que esté
disponible.
Antes de MySQL 4.1, mysql-test-run no
intenta ejecutar su propio servidor por defecto pero intenta
usar su servidor que esté en ejecución. Para cambiar este
comportamiento y hacer que mysql-test-run
arranque su propio servidor, ejecútelo con la opción
--local
.
Puede ejecutar un caso de test individual con
mysql-test/mysql-test-run test_name
.
Si falla un test, debe probar ejecutar
mysql-test-run con la opción
--force
para chequear si algún otro test
falla.
Puede usar el lenguaje mysqltest para crear sus propios casos de test. Desafortunadamente, todavía no hay una documentación completa para el mismo. Sin embargo, puede ojear nuestros casos de uso y usarlos como ejemplo. Los siguientes puntos pueden ayudarle a empezar:
Los tests se encuentran en
mysql-test/t/*.test
Un caso de test consiste en comandos terminados por
;
y es similar a la entrada del cliente
de línea de comandos mysql . Un
comando por defecto es una consulta que se envía a MySQL
server, a no ser que se reconozca como comando interno
(p.e. sleep).
Todas las consultas que producen resultados--por ejemplo,
SELECT
, SHOW
,
EXPLAIN
, etc., debe precederse con
@/path/to/result/file
. El
fichero debe contener los resultados esperados. Una forma
fácil de generar el fichero de resultados es ejecutar
mysqltest -r <t/test-case-name.test
del directorio mysql-test
, y editar
los ficheros resultantes, si es necesario, para ajustarlos
a la salida esperada. En ese caso, sea muy cuidadoso de no
añadir o borrar carácteres invisibles -- asegúrese de
cambiar sólo el texto y/o borrar líneas. Si tiene que
insertar una línea, aségurese que los campos estén
separados por un tabulador, y que hay un tabulador al
final. Si quiere puede usar od -c para
asegurarse que su editor de texto no ha desbaratado nada
durante la edición. Esperamos que nunca tenga que editar
la salida de mysqltest -r ya que sólo
tiene que hacerlo al encontrar un bug.
Para ser consistente con nuestra inicialización, debe
poner sus ficheros resultantes en el directorio
mysql-test/r
y llamarlos
test_name.result
. Si el test produce
más de un resultado, debe usar
test_name.a.result
,
test_name.b.result
, etc.
Si un comando retorna un error, debe especificarlo con
--error error-number
en la línea
anterior al comando. El número de error puede ser una
lista de números de error posibles separados por
',
'.
Si está escribiendo un caso de test de replicación, debe
poner en la primera línea del fichero de test,
source include/master-slave.inc;
. Para
cambiar entre maestro y esclavo, use connection
master;
y connection slave;
.
Si necesita hacer algo en una conexión alternativa, puede
hacer connection master1;
para el
maestro, y connection slave1;
para el
esclavo.
Si necesita hacer algo en un bucle, puede usar algo como esto:
let $1=1000; while ($1) { # do your queries here dec $1; }
Para dormir entre consultas, use el comando sleep. Soporta fracciones de segundo, así que puede usar sleep 1.3;, por ejemplo, para dormir 1.3 segundos.
Para ejecutar el esclavo con opciones adicionales para sus
casos de test, póngalos en el formato de línea de
comandos en
mysql-test/t/test_name-slave.opt
.
Para el maestro, póngalas en
mysql-test/t/test_name-master.opt
.
Si tiene una pregunta sobre la suite de test, o tiene un
caso de test al que contribuir, envíe un email a la lista
de correo MySQL internals
. Consulte
Sección 1.6.1.1, “Las listas de correo de MySQL”. Como esta lista no acepta
adjuntos, debe subir por ftp todos los ficheros relevantes
a: ftp://ftp.mysql.com/pub/mysql/upload/
Si su versión de MySQL no pasa el suite de test debe hacer lo siguiente:
No envíe un reporte de bug antes que haya encontrado todo lo posible sobre qué ha ido mal! Cuando lo haga, por favor use el script mysqlbug para que podamos obtener información sobre el sistema y la versión MySQL. Consulte Sección 1.6.1.3, “Cómo informar de bugs y problemas”.
Asegúrese de incluir la salida de
mysql-test-run, así como los
contenidos de todos los ficheros
.reject
en el directorio
mysql-test/r
.
Si falla un test en la suite, chequee si el test también falla al ejecutarlo en solitario:
cd mysql-test mysql-test-run --local test-name
Si esto falla, debe configurar MySQL con
--with-debug
y ejecutar
mysql-test-run con la opción
--debug
. Si también falla envíe el
fichero de traza var/tmp/master.trace
a ftp://ftp.mysql.com/pub/mysql/upload/
para que podamos examinarlo. Por favor recuerde de incluir
una descripción completa de su sistema, la versión del
binario mysqld y cómo lo compiló.
También intente ejecutar
mysql-test-run con la opción
--force
para ver si hay algún otro test
que falla.
Si ha compilado MySQL usted mismo, chequee nuestro manual para ver cómo compilar MySQL en su plataforma, o preferiblemente, usar uno de los binarios que hemos compilado en http://dev.mysql.com/downloads/. Todos nuestros binarios estándard deben pasar la suite de tests!
Si obtiene un error como Result length
mismatch
o Result content
mismatch
significa que la salida del test no
coincide exactamente con la salida esperada. Este puede
ser un bug en MySQL o que su versión de
mysqld produce resultados ligeramente
distintos bajo algunas circunstancias.
Los resultados de tests fallidos se ponen en un fichero
con el mismo nombre base que el fichero de resultados con
la extensión .reject
. Si su caso de
test falla, debe hacer un diff entre los dos ficheros. Si
no puede ver en qué se distinguen, exemine ambos con
od -c
y compruebe los tamaños.
Si un test falla completamente, debe chequear los ficheros
de logs en el directorio
mysql-test/var/log
para ayudas sobre
qué ha fallado.
Si ha compilado MySQL con depuración puede intentar
depurarlo ejecutando mysql-test-run con
las opciones --gdb
y/o
--debug
. Consulte
Sección D.1.2, “Crear ficheros de traza”.
Si no ha compilado MySQL para depuración debería
hacerlo. Especifique la opción
--with-debug
en
configure. Consulte
Sección 2.8, “Instalación de MySQL usando una distribución de código fuente”.
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
y DROP FUNCTION
.
Consulte Sección 27.2.2, “Sintaxis de CREATE 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 de xxx_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 interfaz
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 a
maybe_null
1
si
xxx()
puede retornar
NULL
. El valor por defecto es
1
si alguno de los argumentos se
declaran maybe_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
, y
1.3
, el valor por defecto es 3, ya que
1.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 por
initid->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()
y
xxx_deinit()
, refiérase a
initid->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. Declare
xxx_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
usar xxx_clear()
en su lugar. Sin
embargo, puede definir xxx_reset()
y
xxx_clear()
si quiere que su UDF
funcione antes y después del cambio de interfaz. (Si no
incluye ambas funciones, la función
xxx_reset()
en muchos casos puede
implementarse internamente llamando
xxx_clear()
para resetear todas las
variables, y luego llamar xxx_add()
para añadir el argumento UDF_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 a
CHAR(0)
antes de llamar a
xxx_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, use
xxx_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
, y
REAL_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 de xxx()
. Por ejemplo,
para especificar que los primeros dos argumentos se
fuercen a una cadena de carácteres de enteros,
respectivamente, haga lo siguiente en
xxx_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 constante i
,
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]
es
0
. Un argumento constante es una
expresión que usa sólo constantes, como
3
o 4*7-2
o
SIN(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 como
args->args[i]
y la longitud de
la cadena es args->lengths[i]
.
No debe asumir que las cadenas de carácteres están
terminadas por null.
Para un argumento de tipo
INT_RESULT
, debe convertir
args->args[i]
a un valor
long long
:
long long int_val; int_val = *((long long*) args->args[i]);
Para un argumento de tipo
REAL_RESULT
, debe convertir
args->args[i]
a un valor
double
:
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 principal
lengths
contiene las longitudes reales
de cualquier argumento de cadenas de carácteres que se
pasa al registro en proceso. Para argumentos de tipos
INT_RESULT
o
REAL_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 llamados udf_example.def
,
udf_example.dsp
, y
udf_example.dsw
.
En el repositorio fuente, busque en el directorio
sql
. Copie
udf_example.cc
de este directorio al
directorio
VC++Files/examples/udf_example
y
renombre el fichero a
udf_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 matriz
sql_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
(donde N
)N
es el número de
argumentos) como el segundo argumento en la matriz
sql_functions[]
y añadir una nueva
función que cree un objeto función en
item_create.cc
. Consulte
"ABS"
y
create_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 parseo
simple_expr
. Para un ejemplo, consulte
todas las ocurrencias de ATAN
en
sql_yacc.yy
para ver cómo se hace.
En item_func.h
, declare una clase
heredando de Item_num_func
o
Item_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 clase Item_str_func
define una
función val()
que ejecuta
atof()
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 asignar maybe_null = 0
si la función principal no puede retornar un valor
NULL
. La función puede chequear si
algunos de los argumentos de la función puede retornar
NULL
chequeando la variable
maybe_null
de los argumentos. Puede
consultar
Item_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
tipo String
, consulte el fichero
sql_string.h
.)
La función ::str()
debe retornar la
cadena de carácteres que tiene el resultado o
(char*) 0
si el resultado es
NULL
.
Todas las funciones de cadenas de carácteres tratan de evitar cualquier reserva de memoria a no ser que sea absolutamente necesario!
En MySQL, puede definir un procedimiento en C++ que pueda acceder
y modificar los datos en una consulta antes de enviarla al
cliente. La modificación puede hacerse registro a registro o a
nivel GROUP BY
.
Hemos creado un procedimiento de ejemplo en MySQL 3.23 para mostrar cómo se hace.
Adicionalmente, recomendamos que consulte
mylua
. Con esto puede usar el lenguaje LUA para
cargar procedimientos en tiempo de ejecución en
mysqld.
analyse([
max_elements
,[max_memory
]])
Este procedimiento se define en
sql/sql_analyse.cc
. Esto examina el
resultado de su consulta y retorna un análisis del resultado:
max_elements
(por defecto 256) es
el máximo número de valores distintos que
analyse
puede tratar por columna. Esto lo
usa analyse
para chequear si el tipo
óptimo de columna debe ser de tipo ENUM
.
max_memory
(por defecto 8192) es
la cantidad máxima de memoria que
analyse
puede reservar por columna
mientras trata de encontrar todos los valores distintos.
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max_elements
,[max_memory
]])
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.