/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * 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 2 of the License, or * (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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_sql_mysql.c: Module for MySQL bindings */ /** * This module implements the MySQL database driver. * * Load this module (and the [[sql:]] module) and pass "mysql" as backend * driver name to [[sql:sql_connect()]]; * * The string describing the database connection (the 2nd argument to * [[sql:sql_connect()]]) is a coma seperated list of the following tokens: * * host={hostname} * user={username} * pass={password} * sock={socket_filename} * db={database_name} * port={tcp_port_number} * * compress (set MySQL CLIENT_COMPRESS flag) * ignore_space (set MySQL CLIENT_IGNORE_SPACE flag) * multi_statements (set MySQL CLIENT_MULTI_STATEMENTS flag) * * Whitespaces are not allowed. E.g.: * * var db = sql_connect("mysql", "host=dbserver,user=dbuser"); * * var r = sql(db, "show databases"); * * foreach i (r) * debug pop r[i]; * */ #include #include #include #include "spl.h" #include "mod_sql.h" #include "compat.h" extern void SPL_ABI(spl_mod_sql_mysql_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_sql_mysql_done)(struct spl_vm *vm, struct spl_module *mod); struct mysql_backend_data { MYSQL mysql; }; static struct spl_node *sql_mysql_query_callback(struct spl_task *task, void *backend_data, const char *query) { struct mysql_backend_data *mbd = backend_data; struct spl_node *result = spl_get(0); MYSQL_ROW row; MYSQL_RES *res; MYSQL_FIELD *fields; unsigned int num_fields; if (mysql_query(&mbd->mysql, query)) goto got_error; res = mysql_store_result(&mbd->mysql); if (res) { num_fields = mysql_num_fields(res); fields = mysql_fetch_fields(res); while ( (row = mysql_fetch_row(res)) ) { struct spl_node *n = spl_get(0); for (unsigned int i=0; i < num_fields; i++) { char *name_base = strrchr(fields[i].name, '.'); name_base = name_base ? name_base+1 : fields[i].name; spl_create(task, n, name_base, row[i] ? SPL_NEW_STRING_DUP(row[i]) : spl_get(0), SPL_CREATE_LOCAL); } spl_create(task, result, NULL, n, SPL_CREATE_LOCAL); } mysql_free_result(res); } else if(mysql_field_count(&mbd->mysql) != 0) goto got_error; return result; got_error: spl_put(task->vm, result); spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "MySQL: SQL Error on '%s': %s!\n", query, mysql_error(&mbd->mysql))), NULL); return 0; } static void sql_mysql_close_callback(struct spl_vm UNUSED(*vm), void *backend_data) { struct mysql_backend_data *mbd = backend_data; mysql_close(&mbd->mysql); free(mbd); } static void sql_mysql_open_callback(struct spl_task *task, struct spl_node *node, const char *data) { const char *original_data = data; const char *config_host = 0; const char *config_user = 0; const char *config_pass = 0; const char *config_sock = 0; const char *config_db = 0; int config_port = 0; int config_flag = 0; while (*data) { int tok_name_len = strcspn(data, "=,"); char *tok_name = my_alloca(tok_name_len+1); memcpy(tok_name, data, tok_name_len); tok_name[tok_name_len] = 0; data += tok_name_len; if (*data == '=') { data++; int tok_value_len = strcspn(data, ","); char *tok_value = my_alloca(tok_value_len+1); memcpy(tok_value, data, tok_value_len); tok_value[tok_value_len] = 0; data += tok_value_len; if (!strcmp("host", tok_name)) config_host = tok_value; else if (!strcmp("user", tok_name)) config_user = tok_value; else if (!strcmp("pass", tok_name)) config_pass = tok_value; else if (!strcmp("sock", tok_name)) config_sock = tok_value; else if (!strcmp("db", tok_name)) config_db = tok_value; else if (!strcmp("port", tok_name)) config_port = atoi(tok_value); else goto parser_error; } else if (!strcmp("compress", tok_name)) config_flag |= CLIENT_COMPRESS; else if (!strcmp("ignore_space", tok_name)) config_flag |= CLIENT_IGNORE_SPACE; else if (!strcmp("multi_statements", tok_name)) #ifdef CLIENT_MULTI_STATEMENTS config_flag |= CLIENT_MULTI_STATEMENTS; #else config_flag |= 0; #endif else { parser_error: spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "MySQL: Error parsing database " "connection description '%s'!\n", original_data)), NULL); return; } if (*data == ',') data++; } struct mysql_backend_data *mbd = malloc(sizeof(struct mysql_backend_data)); mysql_init(&mbd->mysql); if (!mysql_real_connect(&mbd->mysql, config_host, config_user, config_pass, config_db, config_port, config_sock, config_flag)) { spl_clib_exception(task, "SqlEx", "description", SPL_NEW_SPL_STRING(spl_string_printf(0, 0, 0, "MySQL: Can't open database %s: %s!\n", original_data, mysql_error(&mbd->mysql))), NULL); mysql_close(&mbd->mysql); free(mbd); return; } struct sql_hnode_data *hnd = malloc(sizeof(struct sql_hnode_data)); hnd->backend_data = mbd; hnd->query_callback = sql_mysql_query_callback; hnd->close_callback = sql_mysql_close_callback; node->hnode_data = hnd; } /** * This function encodes a string to be used in an MySQL query. I.e. the string * will be put in single quotes and special characters inside the string is * quoted with backslash sequences. * * Always use this function instead of [[sql:encode_sql()]] for quoting strings * in MySQL queries unless you are running MySQL in the ANSI_QUOTES mode. * * This function is designed to be used with the encoding/quoting operator (::). */ // builtin encode_mysql(text) static struct spl_node *handler_encode_mysql(struct spl_task *task, void UNUSED(*data)) { char *text = spl_clib_get_string(task); int text_len = strlen(text); char *newtext = malloc(text_len*2+3); int newtext_len = mysql_escape_string(newtext+1, text, text_len); newtext = realloc(newtext, newtext_len+3); newtext[0] = newtext[newtext_len+1] = '\''; newtext[newtext_len+2] = 0; return SPL_NEW_STRING(newtext); } void SPL_ABI(spl_mod_sql_mysql_init)(struct spl_vm *vm, struct spl_module UNUSED(*mod), int restore) { if (!restore) spl_module_load(vm, "sql", 0); spl_clib_reg(vm, "encode_mysql", handler_encode_mysql, 0); sql_register_backend(vm, "mysql", sql_mysql_open_callback); } void SPL_ABI(spl_mod_sql_mysql_done)(struct spl_vm *vm, struct spl_module UNUSED(*mod)) { sql_unregister_backend(vm, "mysql"); }