tutorial extensiones Mysql

Soujiro

Fanático
Se incorporó
14 Enero 2008
Mensajes
1.401
Estimados, en el trabajo me solicitaron que averiguara como crear extensiones custom para mysql. Después de dar vueltas por la red y jugo durante un día logre mi objetivo así que paso por acá a copy pastear el tutorial simple que hice al respecto dado que no hay mucha información en español al respecto. Se que el codigo no es de los mejores pero cumple el objetivo.

Código:
#ifdef STANDARD
/* STANDARD is defined, don't use any mysql functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef __WIN__
typedef unsigned __int64 ulonglong;    /* Microsofts 64 bit types */
typedef __int64 longlong;
#else
typedef unsigned long long ulonglong;
typedef long long longlong;
#endif /*__WIN__*/
#else
#include <my_global.h>
#include <my_sys.h>
#if defined(MYSQL_SERVER)
#include <m_string.h>        /* To get strmov() */
#else
/* when compiled as standalone */
#include <string.h>
#define strmov(a,b) stpcpy(a,b)
#define bzero(a,b) memset(a,0,b)
#endif
#endif
#include <mysql.h>
#include <ctype.h>

#ifdef _WIN32
/* inet_aton needs winsock library */
#pragma comment(lib, "ws2_32")
#endif

#ifdef HAVE_DLOPEN

#if !defined(HAVE_GETHOSTBYADDR_R) || !defined(HAVE_SOLARIS_STYLE_GETHOST)
static pthread_mutex_t LOCK_hostname;
#endif

//Todo lo anterior solo son las cabeceras requeridas por mysql
//se deve haber instalado las biliotecas de desarrollo de mysql y el codigo 
//debe compilarse con un comando similar al siguiente
//gcc -I/usr/include/mysql -shared -fPIC -o MyTest.so test.c

//informacion recolectada de
//http://blog.loftdigital.com/blog/how-to-write-mysql-functions-in-c 
//http://www.sheepsqueezers.com/joomla/media/documents/MySQLUserDefinedFunctions.pdf
//http://www.codeproject.com/Articles/15643/MySQL-User-Defined-Functions


// Mysql Provee solo un puntero para nuestros datos por lo tanto                                                                        
// Usamos una estructura para almacenarlos                                                 
struct MyTest_data
{
    ulonglong esnulo;
    double    resultado;
};

//Funcion que inicializa nuestra funcion, aca solicitamos memoria
//y realizamos los checkeos correspondientes
my_bool MyTest_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
    //Creamos nuestra variable de tipo puntero para los datos
    struct MyTest_data* data ; 
        
    //Revisamos el numero de argumentos
    if (args->arg_count != 1)
    {
        strcpy(message,"MyTest() requiere al menos un argumento");
        return 1;
    }
    //revisamos el tipo de argumento
    if ((args->arg_type[0] != REAL_RESULT) )
    {
        strcpy(message, "Tipo incorreco: MyTest() requiere un REAL");
        return 1;
    } 
    //inicializamos la memoria necesaria
    if (!(data = (struct MyTest_data*) malloc(sizeof(struct MyTest_data))))
    {
        strcpy(message,"No se puede inicializar la memoria para los Datos");
        return 1;
    }
    
    //El resultado de nuestra funcion puede ser nulo
    initid->maybe_null    = 1;        

    //asignamos un valor inicial a nuestros datos
    data->esnulo=0;
    data->resultado=1.0;
    //asignamos el puntero a la estructura de mysql
    //Se almacena como puntero tipo char en la estructura mysql
    //Por tanto debemos hacer un cast
    initid->ptr = (char*)data;
    return 0;            
}

//Funcion de destruccion para nuestra funcion, basicamente liberamos la memoria
//solicitada en la inicializacion                                                                        
void MyTest_deinit(UDF_INIT *initid)
{
    //Finalizamos la memoria
    free(initid->ptr);
}

//Funcion de limpieza, solo se requiere para las funciones acumulativas
//Como por ejemplo SUM, Count, etc..                                                                       
//Esta función se llama cuando MySQL necesita resetear los resultados resumen. 
//Se llama al comienzo de cada nuevo grupo (sentencia GROUP BY), pero también puede ser
//llamada para restablecer los valores de una consulta donde no hay registros coincidentes .                                                       
//Se Utiliza desde MYSQL 5.0 en adelante
void MyTest_clear(UDF_INIT *initid, char *is_null, char *error)
{
    //casteamos el puntero char de mysql a nuestra estructura
    struct MyTest_data* data = (struct MyTest_data*)initid->ptr;
    //iniciamos los valores por defecto para cada grupo de datos
    if (*error==1) *error=0;
    data->esnulo=0;
    data->resultado=1.0;
}

//Funcion de reseteo,  solo se requiere para las funciones acumulativas
//Como por ejemplo SUM, Count, etc..     
//Cumple la misma funcion que clear pero para Mysql 4.0                                                                       
//Esta para dar retrocompatibilidad a la extension                                                                        
void MyTest_reset(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
    //casteamos el puntero char de mysql a nuestra estructura    
    struct MyTest_data* data = (struct MyTest_data*)initid->ptr;
    //iniciamos los valores por defecto para cada grupo de datos
    if (*error==1) *error=0;
    data->esnulo=0;
    data->resultado=1.0;
}

//Funcion acumulativa, solo se requiere para las funciones acumulativas
//Como por ejemplo SUM, Count, etc..  
//Esta función es llamada para todas las filas que pertenecen 
//al mismo grupo. Se debe utilizar para agregar el valor en el argumento UDF_ARGS 
//a la variable de resumen interna.
void MyTest_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
    //casteamos el puntero char de mysql a nuestra estructura    
    struct MyTest_data* data= (struct MyTest_data*)initid->ptr;
    //casteamos el argumento al tipo de puntero que usamos
    double* pretorno = (double*)args->args[0];
    //verificamos que el puntero no sea null y asignamos los datos a nuestra variable interna
    if (pretorno==NULL) *error=1;
    else data->resultado=data->resultado* (*pretorno+1.0);
}

//Funcion agregada a mysql, en el caso de las funciones acumulativas debe retornar
//el valor de nuestra variable resumen interna. Para los otros casos los calculos se realizan
//en esta funcion.
                                                                      
double MyTest(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
    //Casteamos el puntero char de mysql a nuestra estructura
    struct MyTest_data* data = (struct MyTest_data*)initid->ptr;
    if (*error==1) 
    {
        //esta flag provoca que mi funcion retorne nulo independiente del valor del RETURN
        *is_null = 1;
        return 0.0;
    }
    else return (data->resultado -1.0);
}

#endif /* HAVE_DLOPEN */
 

Zuljin

Fundador
Miembro del Equipo
Fundador
ADMIN
Se incorporó
15 Enero 2004
Mensajes
11.769
Oye [MENTION=47844]Soujiro[/MENTION], ¿por qué debe existir limpieza? Entiendo que es para liberar el objeto de la memoria, pero eso no lo hace automáticamente?
 
Upvote 0

Soujiro

Fanático
Se incorporó
14 Enero 2008
Mensajes
1.401
la funcion clear se encarga de resetear el acumulador custom para cuando usas los group by de forma que funcione correctamente los subtotales, como dice en los comentarios solo es necesario para funciones del tipo sum(), count(), etc Si no es de esas puedes saltartela. Ademas si lo piensas es imposible que el Mysql sepa como "limpiar" tu estructura custom por detras. En mysql 4 la funcion reset se encargaba de eso.
Tecnicamente el struct que use esta demás para el ejemplo (dado que es un vil puntero a double) pero la use por un tema de claridad de código, al fin y al cabo es para que otras personas puedan replicar lo que hice.
 
Upvote 0
Subir