diff --git a/.gitignore b/.gitignore index cbc9f02..9714a91 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Makefile.in *.lo *.o *.la +config.ini diff --git a/Makefile.am b/Makefile.am index 964b79e..21f0092 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I build/m4 -SUBDIRS = src +SUBDIRS = proxy-auth udfs diff --git a/Makefile.decl b/Makefile.decl index f9a7be6..2818033 100644 --- a/Makefile.decl +++ b/Makefile.decl @@ -3,13 +3,30 @@ mysql_LIBS = `mysql_config --libs` mysql_CFLAGS = `mysql_config --cflags` vn_mysql_libdir = /usr/lib/mysql/plugin + +if MYSQL8 + +MYSQLD_SOURCE = /var/mysql/mysql-8.0.15 +vn_mysql_CFLAGS = \ + -Wall -O3 \ + -DMYSQL8=1 \ + -I$(MYSQLD_SOURCE)/include \ + -I$(MYSQLD_SOURCE) \ + -I$(MYSQLD_SOURCE)/bld/include \ + -I$(MYSQLD_SOURCE)libbinlogevents/export \ + $(mysql_CFLAGS) + +else + vn_mysql_CFLAGS = \ -Wall -O3 \ $(mysql_CFLAGS) + +endif + vn_mysql_LDFLAGS = \ -no-undefined \ -module \ -avoid-version \ -export-dynamic \ - $(mysql_LIBS) - + $(mysql_LIBS) \ No newline at end of file diff --git a/README b/README index e69de29..77301a6 100644 --- a/README +++ b/README @@ -0,0 +1 @@ +Take a look at README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2900f1 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ +# MySQL extensions + +This project contains an authentication plugin and functions to extend MySQL +functionality. + +## Compiling + +Install basic tools for compiling. +``` +$ apt-get install build-essential devscripts dh-autoreconf +``` + +Install MySQL and GLib development libraries. +``` +$ apt-get install libmysqld-dev libglib2.0-dev +``` + +Compile and generate Debian package for MySQL 5. +``` +$ debuild -uc -us -b +``` + +Compile and generate Debian package for MySQL 8. +``` +$ debuild --set-envvar MYSQL8=TRUE -uc -us -b +``` + +## Installing + +Install Debian package. +``` +$ dpkg -i vn-mysql_[version]_[arch].deb +``` + +Register plugin and functions into MySQL. +``` +INSTALL PLUGIN proxy_auth SONAME 'proxy_auth.so'; +CREATE AGGREGATE FUNCTION minacum RETURNS INT SONAME 'minacum.so'; +CREATE AGGREGATE FUNCTION multimax RETURNS INT SONAME 'multimax.so'; +CREATE FUNCTION sql_printf RETURNS STRING SONAME 'sql_printf.so'; +``` + +## Uninstalling + +Deregister plugin and functions from MySQL. +``` +UNINSTALL PLUGIN proxy_auth; +DROP FUNCTION IF EXISTS minacum; +DROP FUNCTION IF EXISTS multimax; +DROP FUNCTION IF EXISTS sql_printf; +``` + +## Built with + + * [MySQL](https://dev.mysql.com/doc/refman/8.0/en/extending-mysql.html) + * [GLib](https://developer.gnome.org/glib/) + +## Documentation + + * [Writing plugins](https://dev.mysql.com/doc/refman/8.0/en/writing-plugins.html) + * [Authentication plugins](https://dev.mysql.com/doc/refman/8.0/en/writing-authentication-plugins.html) + * [Adding UDF](https://dev.mysql.com/doc/refman/8.0/en/adding-udf.html) + * [GLib API reference](https://developer.gnome.org/glib/) diff --git a/clean.sh b/clean.sh index 95f28ab..3686253 100755 --- a/clean.sh +++ b/clean.sh @@ -8,6 +8,7 @@ then exit 2 fi +$dir/debian/rules clean rm -rf $dir/build rm -rf $dir/configure rm -rf $dir/gtk-doc.make diff --git a/configure.ac b/configure.ac index c84600e..7d0e995 100644 --- a/configure.ac +++ b/configure.ac @@ -9,14 +9,15 @@ AM_INIT_AUTOMAKE() AM_SILENT_RULES([yes]) # Check for program dependencies -AC_PROG_CC AC_PROG_CXX +AC_PROG_CC PKG_CHECK_MODULES([glib], [glib-2.0]) +AM_CONDITIONAL([MYSQL8], [test ! -z "$MYSQL8"]) + AC_CONFIG_FILES([ Makefile - src/Makefile - src/proxy-auth/Makefile - src/functions/Makefile + proxy-auth/Makefile + udfs/Makefile ]) AC_OUTPUT diff --git a/debian/changelog b/debian/changelog index 690a8e9..8c40c49 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -vn-mysql (1.5.0) unstable; urgency=low +vn-mysql (1.6.0) unstable; urgency=low * Initial Release. diff --git a/debian/control b/debian/control index fe3cc5f..744f3e3 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,13 @@ Source: vn-mysql Section: misc Priority: extra Maintainer: Alejandro T. Colombini Gómez -Build-Depends: build-essential, dh-autoreconf, devscripts, libglib2.0-dev, libmysqld-dev +Build-Depends: build-essential, dh-autoreconf, devscripts, libglib2.0-dev Standards-Version: 3.9.4 Homepage: http://www.verdnatura.es Package: vn-mysql Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends}, mysql-server (>= 5.5.27) | mariadb-galera-server (>= 5.5.27) | mariadb-server (>= 5.5.27), libglib2.0-0 +Depends: mysql-server (>= 5.5.27) | mariadb-server (>= 5.5.27), libglib2.0-0, ${misc:Depends}, ${shlibs:Depends} Description: MySQL plugins This package contains some useful MySQL plugins developed by Verdnatura. diff --git a/doc/install.sql b/doc/install.sql deleted file mode 100644 index e6603f2..0000000 --- a/doc/install.sql +++ /dev/null @@ -1,7 +0,0 @@ - -INSTALL PLUGIN proxy_auth SONAME 'proxy_auth.so'; - -CREATE AGGREGATE FUNCTION minacum RETURNS INT SONAME 'minacum.so'; -CREATE AGGREGATE FUNCTION multimax RETURNS INT SONAME 'multimax.so'; -CREATE FUNCTION sql_printf RETURNS STRING SONAME 'sql_printf.so'; - diff --git a/doc/uninstall.sql b/doc/uninstall.sql deleted file mode 100644 index 3416aab..0000000 --- a/doc/uninstall.sql +++ /dev/null @@ -1,7 +0,0 @@ - -UNINSTALL PLUGIN proxy_auth; - -DROP FUNCTION IF EXISTS minacum; -DROP FUNCTION IF EXISTS multimax; -DROP FUNCTION IF EXISTS sql_printf; - diff --git a/src/proxy-auth/Makefile.am b/proxy-auth/Makefile.am similarity index 94% rename from src/proxy-auth/Makefile.am rename to proxy-auth/Makefile.am index c34ccbc..13f5f41 100644 --- a/src/proxy-auth/Makefile.am +++ b/proxy-auth/Makefile.am @@ -7,7 +7,7 @@ proxy_auth_data_DATA = \ proxy-auth.ini proxy_auth_LTLIBRARIES = proxy_auth.la -proxy_auth_la_SOURCES = proxy-auth.c +proxy_auth_la_SOURCES = proxy-auth.cc proxy_auth_la_LIBADD = \ $(glib_LIBS) \ $(vn_mysql_LIBS) diff --git a/src/proxy-auth/proxy-auth.c b/proxy-auth/proxy-auth.cc similarity index 84% rename from src/proxy-auth/proxy-auth.c rename to proxy-auth/proxy-auth.cc index 7cedc37..d74cdf5 100644 --- a/src/proxy-auth/proxy-auth.c +++ b/proxy-auth/proxy-auth.cc @@ -1,10 +1,10 @@ /* - * Copyright (C) 2013 - Alejandro T. Colombini + * Copyright(C) 2013 - Alejandro T. Colombini * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + *(at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,21 +15,29 @@ * along with this program. If not, see . */ -#define MYSQL_ABI_CHECK - +#include #include #include +#include + +#ifndef MYSQL8 +#define MYSQL_ABI_CHECK +#endif + +//#include #include #include #include -#include +#ifdef MYSQL8 +#include +#include +#endif #define CONFIG_FILE _CONFIG_DIR"/proxy-auth.ini" #define SQL_FILE _SQL_DIR"/proxy-auth.sql" -typedef struct -{ +typedef struct { gboolean initialized; gchar * socket; gchar * host; @@ -49,8 +57,7 @@ typedef struct } ProxyAuth; -typedef struct -{ +typedef struct { MYSQL * conn; gchar * user; gulong user_len; @@ -60,8 +67,7 @@ typedef struct RegexData; ProxyAuth * -proxy_auth_new() -{ +proxy_auth_new() { ProxyAuth * self = g_new(ProxyAuth, 1); self->initialized = FALSE; g_mutex_init(&self->mutex); @@ -69,8 +75,7 @@ proxy_auth_new() } void -proxy_auth_deinit(ProxyAuth * self) -{ +proxy_auth_deinit(ProxyAuth * self) { if (!self->initialized) return; @@ -92,8 +97,7 @@ proxy_auth_deinit(ProxyAuth * self) } void -proxy_auth_free(ProxyAuth * self) -{ +proxy_auth_free(ProxyAuth * self) { g_return_if_fail(self != NULL); proxy_auth_deinit(self); @@ -102,8 +106,7 @@ proxy_auth_free(ProxyAuth * self) } gboolean -proxy_auth_init(ProxyAuth * self) -{ +proxy_auth_init(ProxyAuth * self) { g_return_val_if_fail(self != NULL, FALSE); gboolean res = FALSE; @@ -129,8 +132,7 @@ proxy_auth_init(ProxyAuth * self) GKeyFile * key_file = g_key_file_new(); - if (!g_key_file_load_from_file(key_file, CONFIG_FILE, G_KEY_FILE_NONE, &error)) - { + if (!g_key_file_load_from_file(key_file, CONFIG_FILE, G_KEY_FILE_NONE, &error)) { g_warning("ProxyAuth: Can't open configuration file: %s", error->message); goto end; } @@ -150,8 +152,7 @@ proxy_auth_init(ProxyAuth * self) // Reading the query template - if (!g_file_get_contents(SQL_FILE, &self->queryt, &self->queryt_len, &error)) - { + if (!g_file_get_contents(SQL_FILE, &self->queryt, &self->queryt_len, &error)) { g_warning("ProxyAuth: Can't read the query template: %s", error->message); goto end; } @@ -159,10 +160,9 @@ proxy_auth_init(ProxyAuth * self) // Creates the regular expression self->regex = g_regex_new("#\\w+", - G_REGEX_OPTIMIZE | G_REGEX_MULTILINE, 0, &error); + (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_MULTILINE),(GRegexMatchFlags) 0, &error); - if (error) - { + if (error) { g_warning("ProxyAuth: Can't create the regex: %s", error->message); goto end; } @@ -184,27 +184,22 @@ end: } static gboolean -proxy_auth_regex_func(const GMatchInfo * info, GString * res, gpointer data) -{ +proxy_auth_regex_func(const GMatchInfo * info, GString * res, gpointer data) { RegexData * regex_data = (RegexData *) data; gchar * match = g_match_info_fetch(info, 0); gchar * str = NULL; gulong str_len; - if (!g_strcmp0(match, "#user")) - { + if (!g_strcmp0(match, "#user")) { str = regex_data->user; str_len = regex_data->user_len; - } - else if (!g_strcmp0(match, "#pass")) - { + } else if (!g_strcmp0(match, "#pass")) { str = regex_data->pass; str_len = regex_data->pass_len; } - if (str) - { + if (str) { unsigned long scaped_len; char escaped_str[str_len * 2 + 1]; @@ -216,8 +211,7 @@ proxy_auth_regex_func(const GMatchInfo * info, GString * res, gpointer data) g_string_append_len(res, escaped_str,(gssize) scaped_len); g_string_append_c(res, '\''); - } - else + } else g_string_append(res, match); g_free(match); @@ -225,8 +219,7 @@ proxy_auth_regex_func(const GMatchInfo * info, GString * res, gpointer data) } static gboolean -proxy_auth_reconnect(ProxyAuth * self, MYSQL * conn) -{ +proxy_auth_reconnect(ProxyAuth * self, MYSQL * conn) { g_return_val_if_fail(self != NULL, FALSE); gboolean connected = mysql_real_connect(conn, @@ -240,11 +233,10 @@ proxy_auth_reconnect(ProxyAuth * self, MYSQL * conn) self->last_error = conn_error; return connected; -} +} int -proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info) -{ +proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info) { g_return_val_if_fail(self != NULL, CR_ERROR); int res = CR_ERROR; @@ -257,7 +249,7 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A int user_len = info->user_name_length; if (info->user_name == NULL - &&(user_len = vio->read_packet(vio, &pkt)) < 0) + && (user_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; if (user_len > MYSQL_USERNAME_LENGTH) @@ -270,8 +262,7 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A if (pass_len < 0) return CR_ERROR; - if (!pass_len || *pkt == '\0') - { + if (!pass_len || *pkt == '\0') { info->password_used = PASSWORD_USED_NO; return CR_ERROR; } @@ -286,19 +277,16 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A MYSQL * conn = (MYSQL *) g_async_queue_try_pop(self->conn_pool); - if (!conn) - { + if (!conn) { g_mutex_lock(&self->mutex); - if (self->pool_size < self->max_connections) - { + if (self->pool_size < self->max_connections) { self->pool_size++; g_mutex_unlock(&self->mutex); conn = mysql_init(NULL); - if (!proxy_auth_reconnect(self, conn)) - { + if (!proxy_auth_reconnect(self, conn)) { g_mutex_lock(&self->mutex); self->pool_size--; g_mutex_unlock(&self->mutex); @@ -307,16 +295,13 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A conn = NULL; goto end; } - } - else - { + } else { g_mutex_unlock(&self->mutex); conn = (MYSQL *) g_async_queue_pop(self->conn_pool); } } - switch (mysql_errno(conn)) - { + switch (mysql_errno(conn)) { case CR_SERVER_LOST: case CR_SERVER_GONE_ERROR: if (!proxy_auth_reconnect(self, conn)) @@ -333,10 +318,9 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A regex_data.pass_len = pass_len; query = g_regex_replace_eval(self->regex, - self->queryt, self->queryt_len, 0, 0, proxy_auth_regex_func, ®ex_data, &error); + self->queryt, self->queryt_len, 0,(GRegexMatchFlags) 0, proxy_auth_regex_func, ®ex_data, &error); - if (error) - { + if (error) { g_warning("ProxyAuth: Can't evaluate regex: %s", error->message); goto end; } @@ -349,16 +333,13 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A MYSQL_RES * result; if (!mysql_query(conn, query) - &&(result = mysql_store_result(conn))) - { + && (result = mysql_store_result(conn))) { MYSQL_ROW row = mysql_fetch_row(result); - if (row && row[0]) - { + if (row && row[0]) { unsigned long row_len = mysql_fetch_lengths(result)[0]; - if (row_len > 0 && row_len <= MYSQL_USERNAME_LENGTH) - { + if (row_len > 0 && row_len <= MYSQL_USERNAME_LENGTH) { strcpy(info->external_user, info->user_name); strncpy(info->authenticated_as, row[0], row_len); info->authenticated_as[row_len] = '\0'; @@ -370,8 +351,7 @@ proxy_auth_authenticate(ProxyAuth * self, MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_A } mysql_free_result(result); - } - else + } else g_warning("ProxyAuth: Error executing query: %s", mysql_error(conn)); end: if (conn) @@ -385,21 +365,18 @@ end: ProxyAuth * pauth = NULL; static int -proxy_auth_plugin_main(MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info) -{ +proxy_auth_plugin_main(MYSQL_PLUGIN_VIO * vio, MYSQL_SERVER_AUTH_INFO * info) { return proxy_auth_authenticate(pauth, vio, info); } static int -proxy_auth_plugin_init() -{ +proxy_auth_plugin_init(MYSQL_PLUGIN plugin_info) { pauth = proxy_auth_new(); return proxy_auth_init(pauth) ? 0 : 1; } static int -proxy_auth_plugin_deinit() -{ +proxy_auth_plugin_deinit(MYSQL_PLUGIN plugin_info) { proxy_auth_free(pauth); return 0; } @@ -408,8 +385,7 @@ int generate_auth_string_hash( char *outbuf, unsigned int *buflen, const char *inbuf, - unsigned int inbuflen) -{ + unsigned int inbuflen) { if (*buflen < inbuflen) return 1; @@ -421,8 +397,7 @@ generate_auth_string_hash( int validate_auth_string_hash( char* const inbuf __attribute__((unused)), - unsigned int buflen __attribute__((unused))) -{ + unsigned int buflen __attribute__((unused))) { return 0; } @@ -431,26 +406,26 @@ set_salt( const char* password __attribute__((unused)), unsigned int password_len __attribute__((unused)), unsigned char* salt __attribute__((unused)), - unsigned char* salt_len) -{ + unsigned char* salt_len) { *salt_len = 0; return 0; } static struct st_mysql_auth -proxy_auth_handler = -{ +proxy_auth_handler = { MYSQL_AUTHENTICATION_INTERFACE_VERSION, "mysql_clear_password", // Cleartext plugin required in the client proxy_auth_plugin_main, generate_auth_string_hash, validate_auth_string_hash, set_salt, - AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE + AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE, +#ifdef MYSQL8 + NULL +#endif }; -mysql_declare_plugin(proxy_auth) -{ +mysql_declare_plugin(proxy_auth) { MYSQL_AUTHENTICATION_PLUGIN, &proxy_auth_handler, "proxy_auth", @@ -458,6 +433,9 @@ mysql_declare_plugin(proxy_auth) "Proxy user authentication server-side plugin", PLUGIN_LICENSE_GPL, proxy_auth_plugin_init, +#ifdef MYSQL8 + NULL, +#endif proxy_auth_plugin_deinit, 0x0100, // version 1.0, NULL, diff --git a/src/proxy-auth/proxy-auth.ini b/proxy-auth/proxy-auth.ini similarity index 100% rename from src/proxy-auth/proxy-auth.ini rename to proxy-auth/proxy-auth.ini diff --git a/src/proxy-auth/proxy-auth.pstmt.c b/proxy-auth/proxy-auth.pstmt.c similarity index 100% rename from src/proxy-auth/proxy-auth.pstmt.c rename to proxy-auth/proxy-auth.pstmt.c diff --git a/src/proxy-auth/proxy-auth.sql b/proxy-auth/proxy-auth.sql similarity index 100% rename from src/proxy-auth/proxy-auth.sql rename to proxy-auth/proxy-auth.sql diff --git a/proxy-auth/test/command.sh b/proxy-auth/test/command.sh new file mode 100755 index 0000000..52ef3da --- /dev/null +++ b/proxy-auth/test/command.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +mysql --defaults-file=config.ini --skip-column-names --enable-cleartext-plugin < query.sql + diff --git a/proxy-test/connect.sh b/proxy-auth/test/connect.sh similarity index 100% rename from proxy-test/connect.sh rename to proxy-auth/test/connect.sh diff --git a/proxy-test/proxy-test.sh b/proxy-auth/test/proxy-test.sh similarity index 77% rename from proxy-test/proxy-test.sh rename to proxy-auth/test/proxy-test.sh index 8ef39cd..c2da908 100755 --- a/proxy-test/proxy-test.sh +++ b/proxy-auth/test/proxy-test.sh @@ -1,7 +1,7 @@ #!/bin/bash -CONCURRENCY=400 -NATTEMPS=40 +CONCURRENCY=50 +NATTEMPS=500 I=0 diff --git a/proxy-test/query.sql b/proxy-auth/test/query.sql similarity index 100% rename from proxy-test/query.sql rename to proxy-auth/test/query.sql diff --git a/proxy-test/test.sql b/proxy-auth/test/test.sql similarity index 100% rename from proxy-test/test.sql rename to proxy-auth/test/test.sql diff --git a/proxy-test/command.sh b/proxy-test/command.sh deleted file mode 100755 index e837846..0000000 --- a/proxy-test/command.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -mysql --enable-cleartext-plugin -u test-user --password=1234 < query.sql - diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index dc5ef9e..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ - -SUBDIRS = \ - proxy-auth \ - functions diff --git a/src/functions/minacum.c b/src/functions/minacum.c deleted file mode 100644 index 108228a..0000000 --- a/src/functions/minacum.c +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include - -/* - DROP FUNCTION minacum; - CREATE AGGREGATE FUNCTION minacum RETURNS INT SONAME 'minacum.so'; -*/ - -#define GET_ARG(n) (!args->args[n]) ? 0 : *((long long*) args->args[n]) - -typedef struct _Interval Interval; -struct _Interval { - long long num; - long long sum; - Interval* next; -}; - -long long minacum (UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { - Interval* ival = (Interval*) initid->ptr; - long long num = GET_ARG(2); - long long min = (ival->num != num) ? 0 : 9999999; - long long acum = 0; - while (ival != NULL) { - acum += ival->sum; - if (acum < min) - min = acum; - ival = ival->next; - } - return min; -} - -void minacum_add (UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { - long long num = GET_ARG(0); - long long amount = GET_ARG(1); - Interval* ival = (Interval*) initid->ptr; - Interval* prev = ival; - while (ival != NULL && ival->num < num) { - prev = ival; - ival = ival->next; - } - if (ival == NULL || ival->num > num) { - Interval* new; - if ((new = (Interval*) malloc (sizeof (Interval)))) { - new->num = num; - new->sum = amount; - new->next = ival; - if (prev != ival) - prev->next = new; - else - initid->ptr = (char *) new; - } - } else - ival->sum += amount; -} - -void minacum_clear (UDF_INIT *initid, char *is_null, char *error) { - Interval* ival = (Interval*) initid->ptr; - Interval* cur; - while (ival != NULL) { - cur = ival; - ival = ival->next; - free (cur); - } - initid->ptr = NULL; -} - -my_bool minacum_init (UDF_INIT *initid, UDF_ARGS *args, char *message) { - if (args->arg_count == 3) { - args->arg_type[0] = INT_RESULT; - args->arg_type[1] = INT_RESULT; - args->arg_type[2] = INT_RESULT; - initid->maybe_null = 0; - initid->const_item = 0; - initid->ptr = NULL; - return 0; - } else { - strcpy(message, "minacum must have 3 parameters"); - return 1; - } -} - -void minacum_deinit (UDF_INIT *initid) {} - diff --git a/src/functions/multimax.c b/src/functions/multimax.c deleted file mode 100644 index 06aaabb..0000000 --- a/src/functions/multimax.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include -#include -#include - -#define NAME "multimax" -#define PARAMC 2 - -#define DATA(ptr) ((void **) ptr) -#define INT(ptr) (*((long long *) ptr)) -#define DOUBLE(ptr) (*((double *) ptr)) - -long long multimax (UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) -{ - return INT (DATA (initid->ptr)[1]); -} - -void multimax_add (UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) -{ - size_t set; - void ** data = DATA (initid->ptr); - - if (args->args[0] && data[0]) - { - switch (args->arg_type[0]) - { - case INT_RESULT: - set = INT (args->args[0]) > INT (data[0]); - break; - case REAL_RESULT: - set = DOUBLE (args->args[0]) > DOUBLE (data[0]); - break; - default: // STRING - set = strcmp (args->args[0], data[0]) > 0; - } - } - else - set = data[0] == NULL; - - if (set) - { - int n; - - for (n = 0; n < PARAMC; n++) - { - free (data[n]); - - switch (args->arg_type[0]) - { - case INT_RESULT: - set = sizeof (long long); - data[n] = malloc (set); - break; - case REAL_RESULT: - set = sizeof (double); - data[n] = malloc (set); - break; - default: // STRING - set = args->lengths[0]; - data[n] = malloc (set + 1); - ((char *) data[n])[set] = '\0'; - } - - memcpy (data[n], args->args[n], set); - } - } -} - -void multimax_clear (UDF_INIT *initid, char *is_null, char *error) -{ - void ** data = DATA (initid->ptr); - free (data[0]); - free (data[1]); - data[0] = NULL; - data[1] = NULL; -} - -my_bool multimax_init (UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count == PARAMC) - { - void ** data = malloc (sizeof (void *) * 2); - data[0] = NULL; - data[1] = NULL; - - args->arg_type[1] = INT_RESULT; - initid->ptr = (void *) data; - initid->maybe_null = 0; - initid->const_item = 0; - return 0; - } - else - { - sprintf (message, "%s must have %d parameters", NAME, PARAMC); - return 1; - } -} - -void multimax_deinit (UDF_INIT *initid) -{ - free (initid->ptr); -} - diff --git a/src/functions/sql_printf.c b/src/functions/sql_printf.c deleted file mode 100644 index d3bcf27..0000000 --- a/src/functions/sql_printf.c +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include - -#define NAME "multimax" -#define NPARAMS 1 -#define BLOCK_SIZE 1024 - -typedef struct -{ - int * org_type; - char * data; - unsigned long len; - unsigned long alloc; -} -Buffer; - -static void buffer_append (Buffer *buffer, char *string, unsigned long len) -{ - if (!string) - return; - - unsigned long new_len = buffer->len + len; - - if (new_len > buffer->alloc) - { - buffer->alloc = new_len + BLOCK_SIZE - (new_len % BLOCK_SIZE); - buffer->data = realloc (buffer->data, sizeof (char) * buffer->alloc); - } - - strncpy (&buffer->data[buffer->len], string, len); - buffer->len = new_len; -} - -char * sql_printf (UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) -{ - int i = 1; - unsigned long j = 0; - Buffer *buffer = (Buffer *) initid->ptr; - char *format = args->args[0]; - - if (!format) - { - *is_null = 1; - return NULL; - } - - while (1) - { - char *delimiter = NULL; - unsigned long aux = j; - - while (j < args->lengths[0] && format[j] != '%') - j++; - - buffer_append (buffer, &format[aux], j - aux); - - if (j == args->lengths[0]) - break; - - char c = format[j+1]; - j += 2; - - switch (c) - { - case 't': - delimiter = "`"; - break; - case 'v': - if (buffer->org_type[i] == STRING_RESULT) - delimiter = "'"; - break; - case 's': - break; - case '%': - buffer_append (buffer, "%", 1); - continue; - default: - *error = 1; - return NULL; - } - - if (i >= args->arg_count) - { - *error = 1; - return NULL; - } - - char *arg = args->args[i]; - unsigned long len = args->lengths[i]; - - if (!arg) - { - arg = "NULL"; - len = strlen (arg); - delimiter = NULL; - } - - buffer_append (buffer, delimiter, 1); - buffer_append (buffer, arg, len); - buffer_append (buffer, delimiter, 1); - i++; - } - - *length = buffer->len; - return buffer->data; -} - -my_bool sql_printf_init (UDF_INIT *initid, UDF_ARGS *args, char *message) -{ - if (args->arg_count >= NPARAMS) - { - int i; - Buffer *buffer = malloc (sizeof (Buffer)); - buffer->org_type = malloc (sizeof (int) * args->arg_count); - buffer->alloc = BLOCK_SIZE; - buffer->data = malloc (sizeof (char) * buffer->alloc); - buffer->len = 0; - - for (i = 0; i < args->arg_count; i++) - { - buffer->org_type[i] = args->arg_type[i]; - args->arg_type[i] = STRING_RESULT; - } - - initid->ptr = (void *) buffer; - initid->maybe_null = 1; - initid->const_item = 0; - return 0; - } - else - { - sprintf (message, "%s must have at least %d parameters", NAME, NPARAMS); - return 1; - } -} - -void sql_printf_deinit (UDF_INIT *initid) -{ - Buffer *buffer = (Buffer *) initid->ptr; - free (buffer->org_type); - free (buffer->data); - free (buffer); -} - diff --git a/src/functions/Makefile.am b/udfs/Makefile.am similarity index 81% rename from src/functions/Makefile.am rename to udfs/Makefile.am index 0c680d9..b63cbbd 100644 --- a/src/functions/Makefile.am +++ b/udfs/Makefile.am @@ -4,8 +4,9 @@ include $(top_srcdir)/Makefile.decl # minacum minacum_LTLIBRARIES = minacum.la -minacum_la_SOURCES = minacum.c +minacum_la_SOURCES = minacum.cc minacum_la_LIBADD = $(vn_mysql_LIBS) +minacum_la_CPPFLAGS = $(vn_mysql_CFLAGS) minacum_la_CFLAGS = $(vn_mysql_CFLAGS) minacum_la_LDFLAGS = $(vn_mysql_LDFLAGS) minacumdir = $(vn_mysql_libdir) @@ -13,18 +14,20 @@ minacumdir = $(vn_mysql_libdir) # sql_printf sql_printf_LTLIBRARIES = sql_printf.la -sql_printf_la_SOURCES = sql_printf.c +sql_printf_la_SOURCES = sql_printf.cc sql_printf_la_LIBADD = $(vn_mysql_LIBS) sql_printf_la_CFLAGS = $(vn_mysql_CFLAGS) +sql_printf_la_CPPFLAGS = $(vn_mysql_CFLAGS) sql_printf_la_LDFLAGS = $(vn_mysql_LDFLAGS) sql_printfdir = $(vn_mysql_libdir) # multimax multimax_LTLIBRARIES = multimax.la -multimax_la_SOURCES = multimax.c +multimax_la_SOURCES = multimax.cc multimax_la_LIBADD = $(vn_mysql_LIBS) multimax_la_CFLAGS = $(vn_mysql_CFLAGS) +multimax_la_CPPFLAGS = $(vn_mysql_CFLAGS) multimax_la_LDFLAGS = $(vn_mysql_LDFLAGS) multimaxdir = $(vn_mysql_libdir) @@ -40,4 +43,3 @@ install-data-hook: #$(FILE): $(SRC) # gcc -shared -o $@ $< -fPIC - diff --git a/udfs/minacum.cc b/udfs/minacum.cc new file mode 100644 index 0000000..b84b971 --- /dev/null +++ b/udfs/minacum.cc @@ -0,0 +1,108 @@ +#include +#include +#include +#include + +#define NAME "minacum" +#define N_ARGS 3 + +#define ARG_IVAL 0 +#define ARG_NUM 1 +#define ARG_BASE 2 + +#define INTERVAL(ptr) ((Interval *) ptr) +#define GET_ARG(n) (args->args[n] == NULL ? 0 : *((long long*) args->args[n])) + +extern "C" { + +typedef struct _Interval Interval; +struct _Interval { + long long num; + long long sum; + Interval* next; +}; + +void free_data(UDF_INIT *initid) { + Interval* ival = INTERVAL(initid->ptr); + Interval* cur; + while (ival != NULL) { + cur = ival; + ival = ival->next; + free(cur); + } + initid->ptr = NULL; +} + +long long minacum(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { + Interval* ival = INTERVAL(initid->ptr); + if (ival == NULL) return 0; + + long long min; + long long base = GET_ARG(ARG_BASE); + + if (ival->num == base) { + min = ival->sum; + ival = ival->next; + } else + min = 0; + + long long acum = min; + + while (ival != NULL) { + acum += ival->sum; + if (acum < min) min = acum; + ival = ival->next; + } + + return min; +} + +void minacum_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { + long long num = GET_ARG(ARG_IVAL); + long long amount = GET_ARG(ARG_NUM); + + Interval* ival = INTERVAL(initid->ptr); + Interval* prev = ival; + + while (ival != NULL && ival->num < num) { + prev = ival; + ival = ival->next; + } + + if (ival == NULL || ival->num > num) { + Interval* new_ival = INTERVAL(malloc(sizeof(Interval))); + new_ival->num = num; + new_ival->sum = amount; + new_ival->next = ival; + if (prev != ival) + prev->next = new_ival; + else + initid->ptr = (char *) new_ival; + } else + ival->sum += amount; +} + +void minacum_clear(UDF_INIT *initid, char *is_null, char *error) { + free_data(initid); +} + +bool minacum_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == N_ARGS) { + args->arg_type[ARG_IVAL] = INT_RESULT; + args->arg_type[ARG_NUM] = INT_RESULT; + args->arg_type[ARG_BASE] = INT_RESULT; + initid->maybe_null = 0; + initid->const_item = 0; + initid->ptr = NULL; + return 0; + } else { + sprintf(message, "%s must have %d parameters", NAME, N_ARGS); + return 1; + } +} + +void minacum_deinit(UDF_INIT *initid) { + free_data(initid); +} + +} diff --git a/udfs/multimax.cc b/udfs/multimax.cc new file mode 100644 index 0000000..97bcbdb --- /dev/null +++ b/udfs/multimax.cc @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#define NAME "multimax" +#define N_ARGS 2 + +#define ARG_MAX 0 +#define ARG_VALUE 1 + +#define DATA(ptr) ((Data *) ptr) + +extern "C" { + +#include "value.cc" + +typedef struct { + Value max; + Value value; +} +Data; + +void free_data(UDF_INIT *initid) { + Data* data = (Data *) initid->ptr; + value_set_null(&data->max); + value_set_null(&data->value); +} + +long long multimax(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { + Value * value = &DATA(initid->ptr)->value; + if (value->is_null) { + *is_null = 1; + return 0; + } + return value->val.i; +} + +void multimax_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { + Data *data = (Data *) initid->ptr; + Value max; + value_from_arg(&max, args, ARG_MAX); + + if (value_compare(&max, &data->max) > 0) { + value_copy_arg(&data->max, args, ARG_MAX); + value_copy_arg(&data->value, args, ARG_VALUE); + } +} + +void multimax_clear(UDF_INIT *initid, char *is_null, char *error) { + free_data(initid); +} + +bool multimax_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == N_ARGS) { + Data *data = DATA(malloc(sizeof(Data))); + value_init(&data->max); + value_init(&data->value); + + args->arg_type[ARG_VALUE] = INT_RESULT; + initid->ptr = (char *) data; + initid->maybe_null = 1; + initid->const_item = 0; + return 0; + } else { + sprintf(message, "%s must have %d parameters", NAME, N_ARGS); + return 1; + } +} + +void multimax_deinit(UDF_INIT *initid) { + free_data(initid); + free(initid->ptr); +} + +} diff --git a/udfs/sql_printf.cc b/udfs/sql_printf.cc new file mode 100644 index 0000000..6df97d7 --- /dev/null +++ b/udfs/sql_printf.cc @@ -0,0 +1,135 @@ + +#include +#include +#include + +#define NAME "sql_printf" +#define N_ARGS 1 +#define BLOCK_SIZE 1024 + +extern "C" { + +typedef struct { + int * org_type; + char * data; + unsigned long len; + unsigned long alloc; +} +Buffer; + +static void buffer_append_len(Buffer *buffer, const char *string, unsigned long len) { + if (!string) return; + unsigned long new_len = buffer->len + len; + + if (new_len > buffer->alloc) { + buffer->alloc = new_len + BLOCK_SIZE - (new_len % BLOCK_SIZE); + buffer->data = (char *) realloc(buffer->data, sizeof(char) * buffer->alloc); + } + + strncpy(&buffer->data[buffer->len], string, len); + buffer->len = new_len; +} + +static void buffer_append(Buffer *buffer, const char *string) { + if (!string) return; + buffer_append_len(buffer, string, strlen(string)); +} + +char * sql_printf(UDF_INIT *initid, UDF_ARGS *args, char *result, unsigned long *length, char *is_null, char *error) { + unsigned int i = 1; + unsigned long j = 0; + Buffer *buffer = (Buffer *) initid->ptr; + char *format = args->args[0]; + + if (!format) { + *is_null = 1; + return NULL; + } + + while (1) { + const char *delimiter = NULL; + unsigned long aux = j; + + while (j < args->lengths[0] && format[j] != '%') + j++; + + buffer_append_len(buffer, &format[aux], j - aux); + + if (j == args->lengths[0]) + break; + + char c = format[j+1]; + j += 2; + + switch (c) { + case 't': + delimiter = "`"; + break; + case 'v': + if (buffer->org_type[i] == STRING_RESULT) + delimiter = "'"; + break; + case 's': + break; + case '%': + buffer_append(buffer, "%"); + continue; + default: + *error = 1; + return NULL; + } + + if (i >= args->arg_count) { + *error = 1; + return NULL; + } + + char *arg = args->args[i]; + unsigned long len = args->lengths[i]; + + if (arg) { + buffer_append(buffer, delimiter); + buffer_append_len(buffer, arg, len); + buffer_append(buffer, delimiter); + } else + buffer_append(buffer, "NULL"); + + i++; + } + + *length = buffer->len; + return buffer->data; +} + +bool sql_printf_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count >= N_ARGS) { + unsigned int i; + Buffer *buffer = (Buffer *) malloc(sizeof(Buffer)); + buffer->org_type = (int *) malloc(sizeof(int) * args->arg_count); + buffer->alloc = BLOCK_SIZE; + buffer->data = (char *) malloc(sizeof(char) * buffer->alloc); + buffer->len = 0; + + for (i = 0; i < args->arg_count; i++) { + buffer->org_type[i] = args->arg_type[i]; + args->arg_type[i] = STRING_RESULT; + } + + initid->ptr = (char *) buffer; + initid->maybe_null = 1; + initid->const_item = 0; + return 0; + } else { + sprintf(message, "%s must have at least %d parameters", NAME, N_ARGS); + return 1; + } +} + +void sql_printf_deinit(UDF_INIT *initid) { + Buffer *buffer = (Buffer *) initid->ptr; + free(buffer->org_type); + free(buffer->data); + free(buffer); +} + +} diff --git a/udfs/value.cc b/udfs/value.cc new file mode 100644 index 0000000..131a8bc --- /dev/null +++ b/udfs/value.cc @@ -0,0 +1,97 @@ + +// Value handling functions + +#define INT(ptr) (*((long long *) ptr)) +#define DOUBLE(ptr) (*((double *) ptr)) +#define min(a,b) (a < b ? a : b) + +typedef union { + long long i; + double d; + char * s; +} Val; + +typedef struct { + Val val; + int is_null; + Item_result type; + unsigned long len; + int free; +} Value; + +#define value_ptr(value) (value->is_null ? NULL : (char *) &value->val) + +void value_init(Value * value) { + value->is_null = 1; + value->free = 0; +} + +void value_free_val(Value *value) { + if (value->free) free(value->val.s); +} + +void value_set_null(Value *value) { + value_free_val(value); + value->is_null = 1; +} + +void value_set(Value * value, UDF_ARGS *args, int arg_index, int copy) { + char * arg = args->args[arg_index]; + value->free = 0; + + if (arg != NULL) { + value->is_null = 0; + value->type = args->arg_type[arg_index]; + + switch (value->type) { + case INT_RESULT: + value->val.i = INT(arg); + break; + case REAL_RESULT: + value->val.d = DOUBLE(arg); + break; + default: + value->free = copy; + value->len = args->lengths[arg_index]; + if (copy) { + value->val.s = (char *) malloc(value->len); + memcpy(value->val.s, arg, value->len); + } else + value->val.s = arg; + } + } else + value->is_null = 1; +} + +void value_from_arg(Value * value, UDF_ARGS *args, int arg_index) { + value_set(value, args, arg_index, 0); +} + +void value_copy_arg(Value * value, UDF_ARGS *args, int arg_index) { + value_free_val(value); + value_set(value, args, arg_index, 1); +} + +int value_compare(Value *a, Value *b) { + int cmp; + double real_cmp; + + if (!a->is_null && !b->is_null && a->type == b->type) { + switch (a->type) { + case INT_RESULT: + cmp = a->val.i - b->val.i; + break; + case REAL_RESULT: { + real_cmp = a->val.d - b->val.d; + cmp = real_cmp == 0.0 ? 0 : (real_cmp > 0.0 ? 1 : -1); + break; + } + default: // STRING_RESULT & DECIMAL_RESULT + cmp = strncmp(a->val.s, b->val.s, min(a->len, b->len)); + if (cmp == 0) cmp = a->len - b->len; + } + } else + cmp = b->is_null; + + return cmp; +}