|
|
| version 1.1, 2001/04/04 10:54:33 | version 1.15, 2001/04/26 15:09:08 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: MySQL driver. | Parser MySQL driver. |
| Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) | Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf) | Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf) |
| $Id$ | $Id$ |
| */ | */ |
| #include "config_includes.h" | |
| #include "pa_sql_driver.h" | #include "pa_sql_driver.h" |
| #define NO_CLIENT_LONG_LONG | |
| #include "mysql.h" | |
| #include "ltdl.h" | |
| #define MAX_STRING 0x400 | |
| #define MAX_NUMBER 20 | |
| #if _MSC_VER | |
| # define snprintf _snprintf | |
| #endif | |
| static char *lsplit(char *string, char delim) { | |
| if(string) { | |
| char *v=strchr(string, delim); | |
| if(v) { | |
| *v=0; | |
| return v+1; | |
| } | |
| } | |
| return 0; | |
| } | |
| /** | |
| MySQL server driver | |
| @todo figure out about memory for errors: | |
| static=add multithread locks; | |
| dynamic=who should free it up? | |
| */ | |
| class MySQL_Driver : public SQL_Driver { | class MySQL_Driver : public SQL_Driver { |
| public: | public: |
| MySQL_Driver() : SQL_driver() {} | MySQL_Driver() : SQL_Driver() { |
| } | |
| /// get api version | /// get api version |
| int api_version() { return SQL_API_VERSION; } | int api_version() { return SQL_DRIVER_API_VERSION; } |
| /// initialize driver by loading sql dynamic link library | |
| const char *initialize(const char *dlopen_file_spec) { | |
| return dlink(dlopen_file_spec); | |
| } | |
| /// connect | /// connect |
| virtual void *connect(const char *url) { | void connect( |
| return 0; | char *url, /**< @b user:pass@host[:port]/database/charset |
| 3.23.22b | |
| Currently the only option for character_set_name is cp1251_koi8 */ | |
| void **connection ///< output: MYSQL * | |
| ) { | |
| char *user=url; | |
| char *host=lsplit(user, '@'); | |
| char *db=lsplit(host, '/'); | |
| char *pwd=lsplit(user, ':'); | |
| char *error_pos=0; | |
| char *port_cstr=lsplit(host, ':'); | |
| int port=port_cstr?strtol(port_cstr, &error_pos, 0):0; | |
| char *charset=lsplit(db, '/'); | |
| MYSQL *mysql=mysql_init(NULL); | |
| if(!mysql_real_connect(mysql, | |
| host, user, pwd, db, port?port:MYSQL_PORT, NULL, 0)) | |
| services->_throw(mysql_error(mysql)); | |
| if(charset) { | |
| // set charset | |
| char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8 | |
| strncat(statement, charset, MAX_STRING); | |
| if(mysql_query(mysql, statement)) | |
| services->_throw(mysql_error(mysql)); | |
| (*mysql_store_result)(mysql); // throw out the result [don't need but must call] | |
| } | |
| *(MYSQL **)connection=mysql; | |
| } | } |
| /// disconnect | void disconnect(void *connection) { |
| virtual void disconnect(void *info) { | mysql_close((MYSQL *)connection); |
| ; | } |
| void commit(void *connection) {} | |
| void rollback(void *connection) {} | |
| bool ping(void *connection) { | |
| return mysql_ping((MYSQL *)connection)==0; | |
| } | } |
| unsigned int quote(void *connection, | |
| char *to, const char *from, unsigned int length) { | |
| /* | |
| 3.23.22b | |
| You must allocate the to buffer to be at least length*2+1 bytes long. | |
| (In the worse case, each character may need to be encoded as using two bytes, | |
| and you need room for the terminating null byte.) | |
| it's already UNTAINT_TIMES_BIGGER | |
| */ | |
| return (*mysql_escape_string)(to, from, length); | |
| } | |
| void query(void *connection, | |
| const char *astatement, unsigned long offset, unsigned long limit, | |
| unsigned int *column_count, Cell **columns, | |
| unsigned long *row_count, Cell ***rows) { | |
| MYSQL *mysql=(MYSQL *)connection; | |
| MYSQL_RES *res=NULL; | |
| const char *statement; | |
| if(offset || limit) { | |
| size_t statement_size=strlen(astatement); | |
| char *statement_limited=(char *)services->malloc( | |
| statement_size+MAX_NUMBER*2+8/* limit #,#*/+1); | |
| char *cur=statement_limited; | |
| memcpy(cur, astatement, statement_size); cur+=statement_size; | |
| cur+=sprintf(cur, " limit "); | |
| if(offset) | |
| cur+=snprintf(cur, MAX_NUMBER+1, "%u,", offset); | |
| if(limit) | |
| cur+=snprintf(cur, MAX_NUMBER, "%u", limit); | |
| statement=statement_limited; | |
| } else | |
| statement=astatement; | |
| if(mysql_query(mysql, statement)) | |
| services->_throw(mysql_error(mysql)); | |
| if(!(res=mysql_store_result(mysql)) && mysql_field_count(mysql)) | |
| services->_throw(mysql_error(mysql)); | |
| if(!res) { | |
| // empty result | |
| *row_count=0; | |
| *column_count=0; | |
| return; | |
| } | |
| *column_count=mysql_num_fields(res); | |
| if(!*column_count) // old client | |
| *column_count=mysql_field_count(mysql); | |
| *columns=(Cell *)services->malloc(sizeof(Cell)*(*column_count)); | |
| *row_count=(unsigned long)mysql_num_rows(res); | |
| *rows=(Cell **)services->malloc(sizeof(Cell *)*(*row_count)); | |
| for(unsigned int i=0; i<(*column_count); i++){ | |
| MYSQL_FIELD *field=mysql_fetch_field(res); | |
| size_t size=strlen(field->name); | |
| (*columns)[i].size=size; | |
| (*columns)[i].ptr=services->malloc(size); | |
| memcpy((*columns)[i].ptr, field->name, size); | |
| } | |
| for(unsigned long r=0; r<(*row_count); r++) | |
| if(MYSQL_ROW mysql_row=mysql_fetch_row(res)) { // never false.. | |
| unsigned long *lengths=mysql_fetch_lengths(res); | |
| Cell *row=(Cell *)malloc(sizeof(Cell)*(*column_count)); | |
| (*rows)[r]=row; | |
| for(unsigned int i=0; i<(*column_count); i++){ | |
| size_t size=(size_t)lengths[i]; | |
| row[i].size=size; | |
| row[i].ptr=services->malloc(size); | |
| memcpy(row[i].ptr, mysql_row[i], size); | |
| } | |
| } | |
| mysql_free_result(res); | |
| } | |
| private: // mysql client library funcs | |
| typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init; | |
| typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result; | |
| typedef int (STDCALL *t_mysql_query)(MYSQL *, const char *q); t_mysql_query mysql_query; | |
| typedef char * (STDCALL *t_mysql_error)(MYSQL *); t_mysql_error mysql_error; | |
| static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; } | |
| typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host, | |
| const char *user, | |
| const char *passwd, | |
| const char *db, | |
| unsigned int port, | |
| const char *unix_socket, | |
| unsigned int clientflag); t_mysql_real_connect mysql_real_connect; | |
| typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close; | |
| typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping; | |
| typedef unsigned long (STDCALL *t_mysql_escape_string)(char *to,const char *from, | |
| unsigned long from_length); t_mysql_escape_string mysql_escape_string; | |
| typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result; | |
| typedef unsigned long* (STDCALL *t_mysql_fetch_lengths)(MYSQL_RES *result); t_mysql_fetch_lengths mysql_fetch_lengths; | |
| typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row; | |
| typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field; | |
| typedef my_ulonglong (STDCALL *t_mysql_num_rows)(MYSQL_RES *); t_mysql_num_rows mysql_num_rows; | |
| static my_ulonglong STDCALL subst_mysql_num_rows(MYSQL_RES *res) { return res->row_count; } | |
| typedef unsigned int (STDCALL *t_mysql_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields; | |
| static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->field_count; } | |
| typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_field_count; | |
| static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; } | |
| private: // mysql client library funcs linking | |
| const char *dlink(const char *dlopen_file_spec) { | |
| lt_dlhandle handle=lt_dlopen(dlopen_file_spec); | |
| if (!handle) | |
| return "can not open the dynamic link module"; | |
| #define DSLINK(name, action) \ | |
| name=(t_##name)lt_dlsym(handle, #name); \ | |
| if(!name) \ | |
| action; | |
| #define DLINK(name) DSLINK(name, return "function " #name " was not found") | |
| #define SLINK(name) DSLINK(name, name=subst_##name) | |
| DLINK(mysql_init); | |
| DLINK(mysql_store_result); | |
| DLINK(mysql_query); | |
| SLINK(mysql_error); | |
| DLINK(mysql_real_connect); | |
| DLINK(mysql_close); | |
| DLINK(mysql_ping); | |
| DLINK(mysql_escape_string); | |
| DLINK(mysql_free_result); | |
| DLINK(mysql_fetch_lengths); | |
| DLINK(mysql_fetch_row); | |
| DLINK(mysql_fetch_field); | |
| SLINK(mysql_num_rows); | |
| SLINK(mysql_num_fields); | |
| SLINK(mysql_field_count); | |
| return 0; | |
| } | |
| }; | }; |
| extern "C" SQL_Driver *create() { | extern "C" SQL_Driver *create() { |