现在的位置: 首页 > 综合 > 正文

MySQL and memcached

2014年10月16日 ⁄ 综合 ⁄ 共 5239字 ⁄ 字号 评论关闭

原贴:http://jan.kneschke.de/projects/mysql/udf/

UDF

MySQL and memcached

Get

Compile it with:

$ gcc -DDBUG_OFF -shared -o udf_memcache.so udf_memcache.c /
-g -Wall -O2 -lmemcache `mysql_config --cflags`

Copy udf_memcache.so into a directory which contains the your shared libs (e.g. /usr/local/lib).

Install the functions into MySQL by executing the create-function script:

$ mysql < create-function-memcache.sql

UDF_LUA

You are tired of writing your UDFs for MySQL in C or never wanted to write them in C at all ? How about writing them in lua ?

LUA is

and not to forget lua has extensions for everything:

  • LuaSocket - how about some HTTP requests ?
  • LuaSQL - connecting back to the MySQL Server ?
  • ...

UDF_Lua is registered in the MySQL Forge.

functions

As example we want to read the real startup time of the errorlog.

-- we are run with the permissions of the mysqld
--
-- let's try the read the "ready for connections" from the errorlog and look
-- for the last [Note]:
--
-- 061124 17:28:39 [Note] /usr/sbin/mysqld-max: ready for connections.

local f = assert(io.open(params[1], "r"))
local readysince = nil

while true do
local line = f:read()

if not line then break end

local match = string.match(line, "^([0-9]+ [0-9:]+) %[Note%]")

if match then
readysince = match
end
end
f:close()

return readysince

Let's execute it:

> select lua_file(".../ready-since.lua", "/var/lib/mysql/jan-dev.err") as ready_since;
+-----------------+
| ready_since |
+-----------------+
| 061125 18:10:48 |
+-----------------+

Yeah, that matches the string from the errorlog.

Without a script-file

If shorter script you can also just pass the lua-code around. As usual a string is returned and the parameters are available via the params table.

]> select lua("return params[1]", 1) as echo;
+------+
| echo |
+------+
| 1 |
+------+
1 row in set (0.02 sec)

Aggregation

For Aggregation functions like SUM() you have to provide a function which is called for each row (mysql_udf_aggr) and one which returns the final result (mysql_udf_result).

_G["mysql_udf_aggr"] = function (params)
if nil == rows then
rows = 0
end

rows = rows + params[1]
end
_G["mysql_udf_result"] = function (params)
return rows
end

Running it against the world-database:

root@localhost [world]> select lua_aggr_file("/home/jan/sum.lua", population) from City;
+------------------------------------------------+
| lua_aggr_file("/home/jan/sum.lua", population) |
+------------------------------------------------+
| 1429559884 |
+------------------------------------------------+
1 row in set (0.02 sec)

root@localhost [world]> select sum(population) from City;
+-----------------+
| sum(population) |
+-----------------+
| 1429559884 |
+-----------------+
1 row in set (0.01 sec)

Installation

Requirements:

  • lua 5.1

... and the usual UDF requirements

$ wget http://jan.kneschke.de/projects/mysql/udf/UDF-mysql_udf_lua-0.9.2.tar.gz
$ gzip -cd UDF-mysql_udf_lua-0.9.2.tar.gz | tar xf -
$ cd UDF-mysql_udf_lua-0.9.2
$ ./configure
$ make
## copy to a directory which is checked by dlopen()
# cp ./.libs/mysql_udf_lua.so /usr/lib/
$ mysql
mysql> create function lua returns string soname 'mysql_udf_lua.so';
mysql> create function lua_file returns string soname 'mysql_udf_lua.so';
mysql> create function lua_aggr_file returns string soname 'mysql_udf_lua.so';

Thanks to Hartmuts CodeGenMySQLUDF the UDF is packaged with automake/autoconf.

parameter handling

The parameters of the SELECT call are passed as a lua-table to the functions. The indexing for this table is starting a 1 as it is expected by lua-devs. The type between MySQL and LUA are mapped as expected

return value

Whatever you return is either nil or converted into a string. In case of a execution error NULL will be return and the error will be logged to the mysqld-error-log.

My MySQL Server sends Mail !

For the UDF talk in a few hours I wanted to have a nice and hopefully useless example. It should show how the internals work and what has to be setup even to those who never have used a C-Compiler.

I chose to implement a function to send mail directly from the database which I hope will never be used. Really. It is not a good idea to move application code into the database.

#include <mysql.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

my_bool mail_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
initid->maybe_null = 0;

return 0;
}

long long mail(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error) {
char *prg;
FILE *p;
int i;

if (args->arg_count != 2) {
strcpy(error, "MAIL() needs receipient and body");
return 1;
}

if (args->arg_type[0] != STRING_RESULT ||
args->arg_type[1] != STRING_RESULT) {
strcpy(error, "MAIL() needs receipient and body as string");
return 1;
}

for (i = 0; i < args->lengths[0]; i++) {
char c = args->args[0][i];

if (!((c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c == '@') ||
(c == '.') ||
(c >= '0' && c <= '9') ||
(c == '-' ) ||
(c == '_' ))) {
strcpy(error, "the receipient contains a illegal char");

return -1;
}
}

prg = malloc(strlen("/usr/lib/sendmail ") + args->lengths[0] + 1);
strcpy(prg, "/usr/lib/sendmail ");
strcat(prg, args->args[0]);


p = popen(prg, "w");

free(prg);

if (NULL == p) {
strcpy(error, "opening pipe failed");
return -1;
}


fwrite(args->args[1], args->lengths[1], 1, p);
fclose(p);


*is_null = 0;
*error = 0;

return 0;
}

That's the code which has to be compiled with:

$ gcc -I /usr/local/mysql/include/ -shared -o udf_mail.so mail.c -Wall
$ cp udf_mail.so /usr/lib/

at mysql side:

mysql> create function mail returns integer soname 'udf_mail.so';
mysql> select mail('jan@kneschke.de',
'Subject: UDF Mail/r/n/r/nYou got mail');

抱歉!评论已关闭.