--- sql/mysql/parser3mysql.C 2004/12/23 16:54:52 1.26 +++ sql/mysql/parser3mysql.C 2009/04/08 11:13:00 1.35 @@ -10,7 +10,7 @@ 2001.11.06 numrows on "HP-UX istok1 B.11.00 A 9000/869 448594332 two-user license" 3.23.42 & 4.0.0.alfa never worked, both subst & .sl version returned 0 */ -static const char *RCSId="$Id: parser3mysql.C,v 1.26 2004/12/23 16:54:52 paf Exp $"; +static const char *RCSId="$Id: parser3mysql.C,v 1.35 2009/04/08 11:13:00 misha Exp $"; #include "config_includes.h" @@ -28,36 +28,45 @@ static const char *RCSId="$Id: parser3my # define strcasecmp _stricmp #endif -static char *lsplit(char *string, char delim) { - if(string) { - char *v=strchr(string, delim); - if(v) { +// for mysql < 4.1 +#ifndef CLIENT_MULTI_RESULTS +#define CLIENT_MULTI_RESULTS (1UL << 17) +#define OLD_MYSQL_CLIENT 1 +#endif +#ifndef CLIENT_MULTI_STATEMENTS +#define CLIENT_MULTI_STATEMENTS (1UL << 16) +#undef OLD_MYSQL_CLIENT +#define OLD_MYSQL_CLIENT 1 +#endif + +static char *lsplit(char *string, char delim){ + if(string) { + if(char *v=strchr(string, delim)){ *v=0; return v+1; } - } - return 0; + } + return 0; } -static char *lsplit(char **string_ref, char delim) { - char *result=*string_ref; +static char *lsplit(char **string_ref, char delim){ + char *result=*string_ref; char *next=lsplit(*string_ref, delim); - *string_ref=next; - return result; + *string_ref=next; + return result; } -static char* rsplit(char* string, char delim) { - if(string) { - char* v=strrchr(string, delim); - if(v) { +static char* rsplit(char* string, char delim){ + if(string){ + if(char* v=strrchr(string, delim)){ *v=0; return v+1; } - } - return NULL; + } + return NULL; } -static void toupper_str(char *out, const char *in, size_t size) { +static void toupper_str(char *out, const char *in, size_t size){ while(size--) *out++=(char)toupper(*in++); } @@ -66,42 +75,56 @@ struct Connection { SQL_Driver_services* services; MYSQL* handle; - const char* cstrClientCharset; + const char* client_charset; bool autocommit; }; + /** MySQL server driver */ class MySQL_Driver : public SQL_Driver { public: - MySQL_Driver() : SQL_Driver() { + MySQL_Driver() : SQL_Driver() {} + +#ifndef FREEBSD4 + ~MySQL_Driver() { + if(mysql_server_end) + mysql_server_end(); } +#endif /// get api version int api_version() { return SQL_DRIVER_API_VERSION; } + /// initialize driver by loading sql dynamic link library const char *initialize(char *dlopen_file_spec) { return dlopen_file_spec? dlink(dlopen_file_spec):"client library column is empty"; } + /** connect @param url format: @b user:pass@host[:port]|[/unix/socket]/database? - charset=cp1251_koi8& + charset=value& // transcode by server with command 'SET CHARACTER SET value' + ClientCharset=charset& // transcode by parser timeout=3& - compress=1& - named_pipe=1 - 3.23.22b - Currently the only option for @b character_set_name is cp1251_koi8. - WARNING: must be used only to connect, for buffer doesn't live long + compress=0& + named_pipe=1& + autocommit=1& + multi_statements=0 // allows more then one statement in one query + old_client=1 // simulates 3.xx client. not compatible with multi_statements option + 3.x, 4.0 + only option for charset is cp1251_koi8. + 4.1+ accept not 'cp1251_koi8' but 'cp1251', 'utf8' and much more + it is usable for transcoding using sql server */ void connect( - char *url, - SQL_Driver_services& services, - void **connection_ref ///< output: Connection* - ) { + char *url, + SQL_Driver_services& services, + void **connection_ref ///< output: Connection* + ){ char *user=url; char *s=rsplit(user, '@'); char *host=0; @@ -119,40 +142,63 @@ public: int port=port_cstr?strtol(port_cstr, &error_pos, 0):0; char *options=lsplit(db, '?'); - char *cstrBackwardCompAskServerToTranscode=0; + char *charset=0; + +#ifdef OLD_MYSQL_CLIENT + int client_flag=0; +#else + int client_flag=CLIENT_MULTI_RESULTS; +#endif - Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); + Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); + *connection_ref=&connection; connection.services=&services; - connection.handle=mysql_init(NULL); - connection.cstrClientCharset=0; + connection.handle=mysql_init(NULL); + connection.client_charset=0; connection.autocommit=true; - *connection_ref=&connection; - while(options) { - if(char *key=lsplit(&options, '&')) { - if(*key) { - if(char *value=lsplit(key, '=')) { - if(strcmp(key, "ClientCharset" ) == 0) { + while(options){ + if(char *key=lsplit(&options, '&')){ + if(*key){ + if(char *value=lsplit(key, '=')){ + if(strcmp(key, "ClientCharset")==0){ // transcoding with parser toupper_str(value, value, strlen(value)); - connection.cstrClientCharset=value; - } else if(strcasecmp(key, "charset")==0) { // left for backward compatibility, consider using ClientCharset - cstrBackwardCompAskServerToTranscode=value; - } else if(strcasecmp(key, "timeout")==0) { + connection.client_charset=value; + } else if(strcasecmp(key, "charset")==0){ // transcoding with server + charset=value; + } else if(strcasecmp(key, "timeout")==0){ unsigned int timeout=(unsigned int)atoi(value); if(mysql_options(connection.handle, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0) services._throw(mysql_error(connection.handle)); - } else if(strcasecmp(key, "compress")==0) { + } else if(strcasecmp(key, "compress")==0){ if(atoi(value)) if(mysql_options(connection.handle, MYSQL_OPT_COMPRESS, 0)!=0) services._throw(mysql_error(connection.handle)); - } else if(strcasecmp(key, "named_pipe")==0) { + } else if(strcasecmp(key, "named_pipe")==0){ if(atoi(value)) - if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE , 0)!=0) + if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE, 0)!=0) services._throw(mysql_error(connection.handle)); - } else if(strcasecmp(key, "autocommit")==0) { - if(atoi(value)==0) { + } else if(strcasecmp(key, "autocommit")==0){ + if(atoi(value)==0) connection.autocommit=false; - } + } else if(strcasecmp(key, "multi_statements")==0){ +#ifdef OLD_MYSQL_CLIENT + services._throw("driver was built with old mysql includes so multi_statements option can't be used"); +#else + if(!(client_flag==CLIENT_MULTI_RESULTS || client_flag==CLIENT_MULTI_STATEMENTS)) + services._throw("you can't specify together options old_client and multi_statements"); + if(atoi(value)!=0) + client_flag=CLIENT_MULTI_STATEMENTS; +#endif + } else if(strcasecmp(key, "old_client")==0){ +#ifdef OLD_MYSQL_CLIENT + services._throw("driver was built with old mysql includes so old_client option can't be used"); +#else + if(!(client_flag==CLIENT_MULTI_RESULTS || client_flag==0)) + services._throw("you can't specify together options old_client and multi_statements"); + if(atoi(value)!=0) + client_flag=0; +#endif } else services._throw("unknown connect option" /*key*/); } else @@ -161,55 +207,47 @@ public: } } - if(connection.cstrClientCharset && cstrBackwardCompAskServerToTranscode) - services._throw("use 'ClientCharset' option only, " - "'charset' option is obsolete and should not be used with new 'ClientCharset' option"); - - if(!mysql_real_connect(connection.handle, - host, user, pwd, db, port?port:MYSQL_PORT, unix_socket, 0)) + if(!mysql_real_connect( + connection.handle, + host, user, pwd, db, + port?port:MYSQL_PORT, + unix_socket, + client_flag + ) + ){ services._throw(mysql_error(connection.handle)); + } - if(cstrBackwardCompAskServerToTranscode) { - // set charset - char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8 - strncat(statement, cstrBackwardCompAskServerToTranscode, MAX_STRING); - - exec(connection, statement); + if(charset){ + char statement[MAX_STRING]="SET CHARACTER SET "; + strncat(statement, charset, MAX_STRING); + _exec(connection, statement); } if(!connection.autocommit) - exec(connection, "set autocommit=0"); - } - - void exec(Connection& connection, const char* statement) { - if(mysql_query(connection.handle, statement)) - connection.services->_throw(mysql_error(connection.handle)); - (*mysql_store_result)(connection.handle); // throw out the result [don't need but must call] + _exec(connection, "SET AUTOCOMMIT=0"); } void disconnect(void *aconnection) { Connection& connection=*static_cast(aconnection); - mysql_close(connection.handle); connection.handle=0; } + void commit(void *aconnection) { - //_asm int 3; Connection& connection=*static_cast(aconnection); - if(!connection.autocommit) - exec(connection, "commit"); + _exec(connection, "COMMIT"); } + void rollback(void *aconnection) { Connection& connection=*static_cast(aconnection); - if(!connection.autocommit) - exec(connection, "rollback"); + _exec(connection, "ROLLBACK"); } bool ping(void *aconnection) { Connection& connection=*static_cast(aconnection); - return mysql_ping(connection.handle)==0; } @@ -225,48 +263,51 @@ public: mysql_escape_string(result, from, length); return result; } + void query(void *aconnection, - const char *astatement, - size_t placeholders_count, Placeholder* placeholders, - unsigned long offset, unsigned long limit, - SQL_Driver_query_event_handlers& handlers) { + const char *astatement, + size_t placeholders_count, Placeholder* placeholders, + unsigned long offset, unsigned long limit, + SQL_Driver_query_event_handlers& handlers + ){ Connection& connection=*static_cast(aconnection); SQL_Driver_services& services=*connection.services; - const char* cstrClientCharset=connection.cstrClientCharset; MYSQL_RES *res=NULL; if(placeholders_count>0) services._throw("bind variables not supported (yet)"); - // transcode from $request:charset to connect-string?client_charset - if(cstrClientCharset) { - size_t transcoded_statement_size; - services.transcode(astatement, strlen(astatement), - astatement, transcoded_statement_size, + bool transcode_needed=_transcode_required(connection); + + // transcode query from $request:charset to ?ClientCharset + if(transcode_needed){ + size_t length=strlen(astatement); + services.transcode(astatement, length, + astatement, length, services.request_charset(), - cstrClientCharset); + connection.client_charset); } const char *statement; - if(offset || limit) { + if(offset || limit!=SQL_NO_LIMIT){ size_t statement_size=strlen(astatement); char *statement_limited=(char *)services.malloc_atomic( - statement_size+MAX_NUMBER*2+8/* limit #,#*/+1); + statement_size+MAX_NUMBER*2+8/* LIMIT #,#*/+1); char *cur=statement_limited; memcpy(cur, astatement, statement_size); cur+=statement_size; - cur+=sprintf(cur, " limit "); + cur+=sprintf(cur, " LIMIT "); if(offset) cur+=snprintf(cur, MAX_NUMBER+1, "%u,", offset); - if(limit) + if(limit!=SQL_NO_LIMIT) cur+=snprintf(cur, MAX_NUMBER, "%u", limit); statement=statement_limited; } else statement=astatement; if(mysql_query(connection.handle, statement)) - services._throw(mysql_error(connection.handle)); + _throw(connection, mysql_error(connection.handle)); if(!(res=mysql_store_result(connection.handle)) && mysql_field_count(connection.handle)) - services._throw(mysql_error(connection.handle)); + _throw(connection, mysql_error(connection.handle)); if(!res) // empty result: insert|delete|update|... return; @@ -274,7 +315,7 @@ public: if(!column_count) // old client column_count=mysql_field_count(connection.handle); - if(!column_count) { + if(!column_count){ mysql_free_result(res); services._throw("result contains no columns"); } @@ -288,89 +329,113 @@ public: } for(int i=0; iname); char* strm=(char*)services.malloc_atomic(length+1); memcpy(strm, field->name, length+1); const char* str=strm; - // transcode to $request:charset from connect-string?client_charset - if(cstrClientCharset) { + // transcode column name from ?ClientCharset to $request:charset + if(transcode_needed){ services.transcode(str, length, str, length, - cstrClientCharset, + connection.client_charset, services.request_charset()); } CHECK(handlers.add_column(sql_error, str, length)); } else { - // seen some broken client, - // which reported "44" for column count of response to "select 2+2" - column_count=i; - break; + // seen some broken client, + // which reported "44" for column count of response to "select 2+2" + column_count=i; + break; } } CHECK(handlers.before_rows(sql_error)); - while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) { - CHECK(handlers.add_row(sql_error)); - unsigned long *lengths=mysql_fetch_lengths(res); - for(int i=0; itranscode(aerr_msg, length, + aerr_msg, length, + connection.client_charset, + connection.services->request_charset()); + } + connection.services->_throw(aerr_msg); + } + + bool _transcode_required(Connection& connection){ + return (connection.client_charset && strcmp(connection.client_charset, connection.services->request_charset())!=0); + } + private: // mysql client library funcs - typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init; + typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init; - typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options; + typedef void (STDCALL *t_mysql_server_end)(); t_mysql_server_end mysql_server_end; + + typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options; 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; + 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 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 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; + 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 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; @@ -378,27 +443,31 @@ private: // mysql client library funcs typedef MYSQL_FIELD* (STDCALL *t_mysql_fetch_field)(MYSQL_RES *result); t_mysql_fetch_field mysql_fetch_field; - 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_num_fields)(MYSQL_RES *); t_mysql_num_fields mysql_num_fields; + typedef unsigned int (STDCALL *t_mysql_field_count)(MYSQL *); t_mysql_field_count mysql_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; } + static unsigned int STDCALL subst_mysql_num_fields(MYSQL_RES *res) { return res->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) { if(lt_dlinit()) return lt_dlerror(); - lt_dlhandle handle=lt_dlopen(dlopen_file_spec); - if (!handle) { - if(const char* result=lt_dlerror()) - return result; + lt_dlhandle handle=lt_dlopen(dlopen_file_spec); + + if(!handle){ + if(const char* result=lt_dlerror()) + return result; return "can not open the dynamic link module"; } + #define GLINK(name) \ + name=(t_##name)lt_dlsym(handle, #name); + #define DSLINK(name, action) \ - name=(t_##name)lt_dlsym(handle, #name); \ + GLINK(name) \ if(!name) \ action; @@ -406,6 +475,7 @@ private: // mysql client library funcs l #define SLINK(name) DSLINK(name, name=subst_##name) DLINK(mysql_init); + GLINK(mysql_server_end); DLINK(mysql_options); DLINK(mysql_store_result); DLINK(mysql_query); @@ -426,5 +496,6 @@ private: // mysql client library funcs l }; extern "C" SQL_Driver *SQL_DRIVER_CREATE() { - return new MySQL_Driver(); + static MySQL_Driver Driver; + return &Driver; }