MySQL compatibility, memory leaks fixed, moved to C++, docs

This commit is contained in:
Juan Ferrer 2019-12-02 17:58:23 +01:00
parent b6600f9b33
commit 5d8c6555a4
31 changed files with 583 additions and 454 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ Makefile.in
*.lo
*.o
*.la
config.ini

View File

@ -1,5 +1,5 @@
ACLOCAL_AMFLAGS = -I build/m4
SUBDIRS = src
SUBDIRS = proxy-auth udfs

View File

@ -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)

1
README
View File

@ -0,0 +1 @@
Take a look at README.md

63
README.md Normal file
View File

@ -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/)

View File

@ -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

View File

@ -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

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
vn-mysql (1.5.0) unstable; urgency=low
vn-mysql (1.6.0) unstable; urgency=low
* Initial Release.

4
debian/control vendored
View File

@ -2,13 +2,13 @@ Source: vn-mysql
Section: misc
Priority: extra
Maintainer: Alejandro T. Colombini Gómez <atcolombini@verdnatura.es>
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.

View File

@ -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';

View File

@ -1,7 +0,0 @@
UNINSTALL PLUGIN proxy_auth;
DROP FUNCTION IF EXISTS minacum;
DROP FUNCTION IF EXISTS multimax;
DROP FUNCTION IF EXISTS sql_printf;

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#define MYSQL_ABI_CHECK
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glib-unix.h>
#ifndef MYSQL8
#define MYSQL_ABI_CHECK
#endif
//#include <stdbool.h>
#include <mysql.h>
#include <mysql/errmsg.h>
#include <mysql/plugin_auth.h>
#include <glib-unix.h>
#ifdef MYSQL8
#include <ctype.h>
#include <mysql/plugin.h>
#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, &regex_data, &error);
self->queryt, self->queryt_len, 0,(GRegexMatchFlags) 0, proxy_auth_regex_func, &regex_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,

4
proxy-auth/test/command.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
mysql --defaults-file=config.ini --skip-column-names --enable-cleartext-plugin < query.sql

View File

@ -1,7 +1,7 @@
#!/bin/bash
CONCURRENCY=400
NATTEMPS=40
CONCURRENCY=50
NATTEMPS=500
I=0

View File

@ -1,4 +0,0 @@
#!/bin/bash
mysql --enable-cleartext-plugin -u test-user --password=1234 < query.sql

View File

@ -1,4 +0,0 @@
SUBDIRS = \
proxy-auth \
functions

View File

@ -1,84 +0,0 @@
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>
/*
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) {}

View File

@ -1,103 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#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);
}

View File

@ -1,146 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#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);
}

View File

@ -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

108
udfs/minacum.cc Normal file
View File

@ -0,0 +1,108 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <mysql/mysql.h>
#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);
}
}

76
udfs/multimax.cc Normal file
View File

@ -0,0 +1,76 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#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);
}
}

135
udfs/sql_printf.cc Normal file
View File

@ -0,0 +1,135 @@
#include <stdio.h>
#include <string.h>
#include <mysql/mysql.h>
#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);
}
}

97
udfs/value.cc Normal file
View File

@ -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;
}