--- sql/mysql/parser3mysql.C 2012/08/31 08:33:29 1.44 +++ sql/mysql/parser3mysql.C 2020/01/18 20:59:24 1.63 @@ -1,7 +1,7 @@ /** @file Parser MySQL driver. - Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) + Copyright (c) 2001-2019 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) @@ -15,7 +15,7 @@ #include "pa_sql_driver.h" -volatile const char * IDENT_PARSER3MYSQL_C="$Id: parser3mysql.C,v 1.44 2012/08/31 08:33:29 moko Exp $" IDENT_PA_SQL_DRIVER_H; +volatile const char * IDENT_PARSER3MYSQL_C="$Id: parser3mysql.C,v 1.63 2020/01/18 20:59:24 moko Exp $" IDENT_PA_SQL_DRIVER_H; #define NO_CLIENT_LONG_LONG #include "mysql.h" @@ -24,24 +24,11 @@ volatile const char * IDENT_PARSER3MYSQL #define MAX_STRING 0x400 #define MAX_NUMBER 20 -#if _MSC_VER +#ifdef _MSC_VER # define snprintf _snprintf # define strcasecmp _stricmp #endif -// for mysql < 4.1 -#if !defined(CLIENT_MULTI_RESULTS) || !defined(CLIENT_MULTI_STATEMENTS) -# define OLD_MYSQL_CLIENT 1 -#endif - -#ifndef CLIENT_MULTI_RESULTS -# define CLIENT_MULTI_RESULTS (1UL << 17) -#endif - -#ifndef CLIENT_MULTI_STATEMENTS -# define CLIENT_MULTI_STATEMENTS (1UL << 16) -#endif - static char *lsplit(char *string, char delim){ if(string) { if(char *v=strchr(string, delim)){ @@ -78,8 +65,10 @@ inline static bool is_column_transcode_r switch(type) { case MYSQL_TYPE_NULL: - case MYSQL_TYPE_DECIMAL: +#ifdef FIELD_TYPE_NEWDECIMAL case MYSQL_TYPE_NEWDECIMAL: +#endif + case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: @@ -88,7 +77,9 @@ inline static bool is_column_transcode_r 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: @@ -103,15 +94,16 @@ inline static bool is_column_transcode_r case MYSQL_TYPE_LONG_BLOB: return false; break; + default: + return true; } - return true; } -inline static const char* strdup(SQL_Driver_services& services, char* str, size_t length) { +inline static 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; + return strm; } struct Connection { @@ -131,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; } @@ -150,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; @@ -181,28 +159,27 @@ 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; - while(options){ - if(char *key=lsplit(&options, '&')){ - if(*key){ + while(1){ + char *next_host=lsplit(host, ','); + char *host_options=next_host && options ? strdup(services, options, strlen(options)) : options; + + char *port_cstr=lsplit(host, ':'); + int port=port_cstr?strtol(port_cstr, &error_pos, 0):0; + + while(host_options){ + char *key=lsplit(&host_options, '&'); + if(key && *key){ if(char *value=lsplit(key, '=')){ if(strcmp(key, "ClientCharset")==0){ // transcoding with parser toupper_str(value, value, strlen(value)); @@ -229,44 +206,32 @@ public: 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 if(strcasecmp(key, "config_file")==0){ + if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_FILE, value)!=0) + services._throw(mysql_error(connection.handle)); + } else if(strcasecmp(key, "config_group")==0){ + if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_GROUP, value)!=0) + services._throw(mysql_error(connection.handle)); } else services._throw("unknown connect option" /*key*/); - } else + } else services._throw("connect option without =value" /*key*/); } } - } - 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(mysql_real_connect(connection.handle, host, user, pwd, db, port, unix_socket, client_flag)) + break; + + if(!next_host) + services._throw(mysql_error(connection.handle)); + + host=next_host; } if(charset){ - char statement[MAX_STRING+1]="SET CHARACTER SET "; + char statement[MAX_STRING+1]="SET NAMES "; strncat(statement, charset, MAX_STRING); _exec(connection, statement); } @@ -369,7 +334,7 @@ public: MYSQL_RES *res=NULL; if(placeholders_count>0) - services._throw("bind variables not supported (yet)"); + services._throw("bind variables not supported yet"); bool transcode_needed=_transcode_required(connection); @@ -378,10 +343,7 @@ public: if(transcode_needed) { statement_size=strlen(astatement); // transcode query from $request:charset to ?ClientCharset - services.transcode(astatement, statement_size, - astatement, statement_size, - services.request_charset(), - connection.client_charset); + services.transcode(astatement, statement_size, astatement, statement_size, services.request_charset(), connection.client_charset); } const char *statement; @@ -403,92 +365,97 @@ public: if(mysql_query(connection.handle, statement)) _throw(connection, mysql_error(connection.handle)); - if(!(res=mysql_store_result(connection.handle)) && mysql_field_count(connection.handle)) - _throw(connection, mysql_error(connection.handle)); - if(!res) // empty result: insert|delete|update|... - return; - size_t column_count=mysql_num_fields(res); - if(!column_count) // old client - 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; \ - } - -#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()); - ) - } else { - // without transcoding - DO_FETCH_FIELDS() - CHECK(handlers.before_rows(sql_error)); - DO_FETCH_ROWS() - } -cleanup: - if(transcode_column) - delete transcode_column; - mysql_free_result(res); - if(failed) - services._throw(sql_error); + if(res=mysql_store_result(connection.handle)){ + + size_t column_count=mysql_num_fields(res); + if(!column_count) // old client + column_count=mysql_field_count(connection.handle); + + if(!column_count){ + mysql_free_result(res); + services._throw("result contains no columns"); + } + + SQL_Error sql_error; + +#define CHECK(afailed) \ + if(afailed) { \ + mysql_free_result(res); \ + services._throw(sql_error); \ + } + +#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( + // transcode cell's value from ?ClientCharset to $request:charset + if(transcode_column[i]) + services.transcode(str, length, str, length, connection.client_charset, services.request_charset()); + ) +#ifdef _MSC_VER + services.realloc(transcode_column,0); +#endif + } else { + // without transcoding + DO_FETCH_FIELDS({}) + CHECK(handlers.before_rows(sql_error)); + DO_FETCH_ROWS({}) + } + + mysql_free_result(res); + + } else { + if(mysql_field_count(connection.handle)) + _throw(connection, mysql_error(connection.handle)); + // empty result: insert|delete|update|... + } + + next_result = mysql_next_result(connection.handle); + if (next_result > 0) + _throw(connection, mysql_error(connection.handle)); + } while (next_result == 0); } private: @@ -501,10 +468,7 @@ private: void _throw(Connection& connection, const char* aerr_msg) { size_t length=strlen(aerr_msg); if(length && _transcode_required(connection)) { - connection.services->transcode(aerr_msg, length, - aerr_msg, length, - connection.client_charset, - connection.services->request_charset()); + connection.services->transcode(aerr_msg, length, aerr_msg, length, connection.client_charset, connection.services->request_charset()); } connection.services->_throw(aerr_msg); } @@ -515,47 +479,41 @@ private: private: // mysql client library funcs - typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init; - - typedef void (STDCALL *t_mysql_server_end)(); t_mysql_server_end mysql_server_end; + typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *); t_mysql_init mysql_init; + + 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 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 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 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; + typedef unsigned int (STDCALL *t_mysql_next_result)(MYSQL *); t_mysql_next_result mysql_next_result; + + 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; } + static unsigned int STDCALL subst_mysql_next_result(MYSQL *mysql) { return -1; } private: // mysql client library funcs linking @@ -601,6 +559,7 @@ private: // mysql client library funcs l DLINK(mysql_fetch_field); SLINK(mysql_num_fields); SLINK(mysql_field_count); + SLINK(mysql_next_result); return 0; }