--- sql/mysql/parser3mysql.C 2009/04/08 11:13:00 1.35 +++ sql/mysql/parser3mysql.C 2017/08/01 16:50:13 1.52 @@ -1,21 +1,22 @@ /** @file Parser MySQL driver. - Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) - 2001.07.30 using MySQL 3.23.22b + 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" + 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.35 2009/04/08 11:13:00 misha Exp $"; #include "config_includes.h" #include "pa_sql_driver.h" +volatile const char * IDENT_PARSER3MYSQL_C="$Id: parser3mysql.C,v 1.52 2017/08/01 16:50:13 moko Exp $" IDENT_PA_SQL_DRIVER_H; + #define NO_CLIENT_LONG_LONG #include "mysql.h" #include "ltdl.h" @@ -28,17 +29,6 @@ static const char *RCSId="$Id: parser3my # define strcasecmp _stricmp #endif -// 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)){ @@ -71,6 +61,51 @@ static void toupper_str(char *out, const *out++=(char)toupper(*in++); } +inline static bool is_column_transcode_required(enum_field_types type) { + switch(type) { + case MYSQL_TYPE_NULL: + +#ifdef FIELD_TYPE_NEWDECIMAL + case MYSQL_TYPE_NEWDECIMAL: +#endif + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + + case MYSQL_TYPE_TINY: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: +#ifdef FIELD_TYPE_BIT + case MYSQL_TYPE_BIT: +#endif + + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TIMESTAMP: + + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + return false; + break; + default: + return true; + } +} + +inline static const char* strdup(SQL_Driver_services& services, char* str, size_t length) { + char *strm=(char*)services.malloc_atomic(length+1); + memcpy(strm, str, length); + strm[length]=0; + return (const char*)strm; +} + struct Connection { SQL_Driver_services* services; @@ -88,13 +123,6 @@ public: 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; } @@ -107,24 +135,17 @@ public: /** connect @param url format: @b user:pass@host[:port]|[/unix/socket]/database? - charset=value& // transcode by server with command 'SET CHARACTER SET value' + charset=value& // transcode by server with command 'SET NAMES value' ClientCharset=charset& // transcode by parser timeout=3& 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 + 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* - ){ + void connect(char *url, SQL_Driver_services& services, void **connection_ref /*< output: Connection* */){ char *user=url; char *s=rsplit(user, '@'); char *host=0; @@ -138,25 +159,20 @@ public: char *db=lsplit(s, '/'); 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 *options=lsplit(db, '?'); - 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_ref=&connection; connection.services=&services; connection.handle=mysql_init(NULL); - connection.client_charset=0; + connection.client_charset=0; connection.autocommit=true; + char *port_cstr=lsplit(host, ':'); + int port=port_cstr?strtol(port_cstr, &error_pos, 0):0; + while(options){ if(char *key=lsplit(&options, '&')){ if(*key){ @@ -178,27 +194,16 @@ public: if(atoi(value)) if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE, 0)!=0) services._throw(mysql_error(connection.handle)); + } else if(strcasecmp(key, "local_infile")==0){ + if(atoi(value)) + if(mysql_options(connection.handle, MYSQL_OPT_LOCAL_INFILE, 0)!=0) + services._throw(mysql_error(connection.handle)); } 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 @@ -207,19 +212,12 @@ public: } } - if(!mysql_real_connect( - connection.handle, - host, user, pwd, db, - port?port:MYSQL_PORT, - unix_socket, - client_flag - ) - ){ + if(!mysql_real_connect(connection.handle, host, user, pwd, db, port, unix_socket, CLIENT_MULTI_RESULTS)){ services._throw(mysql_error(connection.handle)); } if(charset){ - char statement[MAX_STRING]="SET CHARACTER SET "; + char statement[MAX_STRING+1]="SET NAMES "; strncat(statement, charset, MAX_STRING); _exec(connection, statement); } @@ -251,16 +249,63 @@ public: return mysql_ping(connection.handle)==0; } - const char* quote(void *aconnection, const char *from, unsigned int length) { + // charset here is services.request_charset(), not connection.client_charset + // thus we can't use the sql server quoting support + const char* quote(void *aconnection, const char *str, unsigned int length) { + const char* from; + const char* from_end=str+length; + + size_t quoted=0; + + for(from=str; from(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.) - */ - char *result=(char*)connection.services->malloc_atomic(length*2+1); - mysql_escape_string(result, from, length); + char *result=(char*)connection.services->malloc_atomic(length + quoted + 1); + char *to = result; + + for(from=str; fromname); - char* strm=(char*)services.malloc_atomic(length+1); - memcpy(strm, field->name, length+1); - const char* str=strm; - - // transcode column name from ?ClientCharset to $request:charset - if(transcode_needed){ +#define DO_FETCH_FIELDS(transcode_column_name) { \ + for(size_t i=0; iname_length; \ + const char* str=strdup(services, field->name, length); \ + transcode_column_name \ + CHECK(handlers.add_column(sql_error, str, length)); \ + } else { \ + /* seen broken client, that reported "44" column count for "select 2+2" */ \ + column_count=i; \ + break; \ + } \ + } \ + } + +#define DO_FETCH_ROWS(transcode_cell_value) { \ + while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) { \ + CHECK(handlers.add_row(sql_error)); \ + unsigned long *lengths=mysql_fetch_lengths(res); \ + for(size_t i=0; itype); + // transcode column's name from ?ClientCharset to $request:charset + services.transcode(str, length, + str, length, + connection.client_charset, + services.request_charset()); + ) + CHECK(handlers.before_rows(sql_error)); + DO_FETCH_ROWS( + if(transcode_column[i]) + // transcode cell's value from ?ClientCharset to $request:charset services.transcode(str, length, str, length, 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; - } - } - - 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; inet.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 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 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; - 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; } + 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 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; + + 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(); + if(lt_dlinit()){ + if(const char* result=lt_dlerror()) + return result; + return "can not prepare to dynamic loading"; + } lt_dlhandle handle=lt_dlopen(dlopen_file_spec);