--- sql/mysql/parser3mysql.C 2001/10/02 14:52:10 1.2 +++ sql/mysql/parser3mysql.C 2004/12/23 16:54:52 1.26 @@ -1,13 +1,16 @@ /** @file Parser MySQL driver. - Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com) + Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) - Author: Alexander Petrosyan (http://design.ru/paf) + Author: Alexandr Petrosian (http://paf.design.ru) 2001.07.30 using MySQL 3.23.22b + + 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.2 2001/10/02 14:52:10 parser Exp $"; +static const char *RCSId="$Id: parser3mysql.C,v 1.26 2004/12/23 16:54:52 paf Exp $"; #include "config_includes.h" @@ -43,6 +46,30 @@ static char *lsplit(char **string_ref, c return result; } +static char* rsplit(char* string, char delim) { + if(string) { + char* v=strrchr(string, delim); + if(v) { + *v=0; + return v+1; + } + } + return NULL; +} + +static void toupper_str(char *out, const char *in, size_t size) { + while(size--) + *out++=(char)toupper(*in++); +} + +struct Connection { + SQL_Driver_services* services; + + MYSQL* handle; + const char* cstrClientCharset; + bool autocommit; +}; + /** MySQL server driver */ @@ -55,12 +82,12 @@ public: /// get 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) { + const char *initialize(char *dlopen_file_spec) { return dlopen_file_spec? dlink(dlopen_file_spec):"client library column is empty"; } /** connect - @param used_only_in_connect_url + @param url format: @b user:pass@host[:port]|[/unix/socket]/database? charset=cp1251_koi8& timeout=3& @@ -71,12 +98,12 @@ public: WARNING: must be used only to connect, for buffer doesn't live long */ void connect( - char *used_only_in_connect_url, + char *url, SQL_Driver_services& services, - void **connection ///< output: MYSQL * + void **connection_ref ///< output: Connection* ) { - char *user=used_only_in_connect_url; - char *s=lsplit(user, '@'); + char *user=url; + char *s=rsplit(user, '@'); char *host=0; char *unix_socket=0; if(s && s[0]=='[') { // unix socket @@ -92,28 +119,40 @@ public: int port=port_cstr?strtol(port_cstr, &error_pos, 0):0; char *options=lsplit(db, '?'); - char *charset=0; + char *cstrBackwardCompAskServerToTranscode=0; - MYSQL *mysql=mysql_init(NULL); + Connection& connection=*(Connection *)services.malloc(sizeof(Connection)); + connection.services=&services; + connection.handle=mysql_init(NULL); + connection.cstrClientCharset=0; + connection.autocommit=true; + *connection_ref=&connection; while(options) { if(char *key=lsplit(&options, '&')) { if(*key) { if(char *value=lsplit(key, '=')) { - if(strcasecmp(key, "charset")==0) { - charset=value; + if(strcmp(key, "ClientCharset" ) == 0) { + 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) { unsigned int timeout=(unsigned int)atoi(value); - if(mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0) - services._throw(mysql_error(mysql)); + 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) { if(atoi(value)) - if(mysql_options(mysql, MYSQL_OPT_COMPRESS, 0)!=0) - services._throw(mysql_error(mysql)); + if(mysql_options(connection.handle, MYSQL_OPT_COMPRESS, 0)!=0) + services._throw(mysql_error(connection.handle)); } else if(strcasecmp(key, "named_pipe")==0) { if(atoi(value)) - if(mysql_options(mysql, MYSQL_OPT_NAMED_PIPE , 0)!=0) - services._throw(mysql_error(mysql)); + 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) { + connection.autocommit=false; + } } else services._throw("unknown connect option" /*key*/); } else @@ -122,57 +161,96 @@ public: } } - if(!mysql_real_connect(mysql, + 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)) - services._throw(mysql_error(mysql)); + services._throw(mysql_error(connection.handle)); - if(charset) { + if(cstrBackwardCompAskServerToTranscode) { // set charset char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8 - strncat(statement, charset, MAX_STRING); + strncat(statement, cstrBackwardCompAskServerToTranscode, 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] + exec(connection, statement); } - *(MYSQL **)connection=mysql; + 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] + } + + void disconnect(void *aconnection) { + Connection& connection=*static_cast(aconnection); + + mysql_close(connection.handle); + connection.handle=0; } - void disconnect(void *connection) { - mysql_close((MYSQL *)connection); + void commit(void *aconnection) { + //_asm int 3; + Connection& connection=*static_cast(aconnection); + + if(!connection.autocommit) + exec(connection, "commit"); } - void commit(SQL_Driver_services&, void *) {} - void rollback(SQL_Driver_services&, void *) {} + void rollback(void *aconnection) { + Connection& connection=*static_cast(aconnection); - bool ping(SQL_Driver_services&, void *connection) { - return mysql_ping((MYSQL *)connection)==0; + if(!connection.autocommit) + exec(connection, "rollback"); } - unsigned int quote( - SQL_Driver_services&, void *connection, - char *to, const char *from, unsigned int length) { + bool ping(void *aconnection) { + Connection& connection=*static_cast(aconnection); + + return mysql_ping(connection.handle)==0; + } + + const char* quote(void *aconnection, const char *from, unsigned int length) { + Connection& connection=*static_cast(aconnection); /* 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); + char *result=(char*)connection.services->malloc_atomic(length*2+1); + mysql_escape_string(result, from, length); + return result; } - void query( - SQL_Driver_services& services, void *connection, - const char *astatement, unsigned long offset, unsigned long limit, + 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) { - - MYSQL *mysql=(MYSQL *)connection; + 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, + services.request_charset(), + cstrClientCharset); + } + const char *statement; if(offset || limit) { size_t statement_size=strlen(astatement); - char *statement_limited=(char *)services.malloc( + char *statement_limited=(char *)services.malloc_atomic( statement_size+MAX_NUMBER*2+8/* limit #,#*/+1); char *cur=statement_limited; memcpy(cur, astatement, statement_size); cur+=statement_size; @@ -185,50 +263,83 @@ public: } 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(mysql_query(connection.handle, statement)) + services._throw(mysql_error(connection.handle)); + if(!(res=mysql_store_result(connection.handle)) && mysql_field_count(connection.handle)) + services._throw(mysql_error(connection.handle)); if(!res) // empty result: insert|delete|update|... return; int column_count=mysql_num_fields(res); if(!column_count) // old client - column_count=mysql_field_count(mysql); + column_count=mysql_field_count(connection.handle); if(!column_count) { mysql_free_result(res); services._throw("result contains no columns"); } + + bool failed=false; + SQL_Error sql_error; +#define CHECK(afailed) \ + if(afailed) { \ + failed=true; \ + goto cleanup; \ + } for(int i=0; iname); - void *ptr=services.malloc(size); - memcpy(ptr, field->name, size); - handlers.add_column(ptr, size); + if(MYSQL_FIELD *field=mysql_fetch_field(res)) { + size_t length=strlen(field->name); + 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) { + services.transcode(str, length, + str, length, + cstrClientCharset, + 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; + } } - handlers.before_rows(); + CHECK(handlers.before_rows(sql_error)); - if(unsigned long row_count=(unsigned long)mysql_num_rows(res)) - for(unsigned long r=0; rrow_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; } @@ -279,9 +387,15 @@ private: // mysql client library funcs 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 (!handle) { + if(const char* result=lt_dlerror()) + return result; + return "can not open the dynamic link module"; + } #define DSLINK(name, action) \ name=(t_##name)lt_dlsym(handle, #name); \ @@ -304,7 +418,6 @@ private: // mysql client library funcs l 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;