|
|
| version 1.44, 2012/08/31 08:33:29 | version 1.57, 2019/09/05 15:45:13 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser MySQL driver. | Parser MySQL driver. |
| Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) | Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| Line 29 volatile const char * IDENT_PARSER3MYSQL | Line 29 volatile const char * IDENT_PARSER3MYSQL |
| # define strcasecmp _stricmp | # define strcasecmp _stricmp |
| #endif | #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){ | static char *lsplit(char *string, char delim){ |
| if(string) { | if(string) { |
| if(char *v=strchr(string, delim)){ | if(char *v=strchr(string, delim)){ |
| Line 78 inline static bool is_column_transcode_r | Line 65 inline static bool is_column_transcode_r |
| switch(type) { | switch(type) { |
| case MYSQL_TYPE_NULL: | case MYSQL_TYPE_NULL: |
| case MYSQL_TYPE_DECIMAL: | #ifdef FIELD_TYPE_NEWDECIMAL |
| case MYSQL_TYPE_NEWDECIMAL: | case MYSQL_TYPE_NEWDECIMAL: |
| #endif | |
| case MYSQL_TYPE_DECIMAL: | |
| case MYSQL_TYPE_FLOAT: | case MYSQL_TYPE_FLOAT: |
| case MYSQL_TYPE_DOUBLE: | case MYSQL_TYPE_DOUBLE: |
| Line 88 inline static bool is_column_transcode_r | Line 77 inline static bool is_column_transcode_r |
| case MYSQL_TYPE_LONG: | case MYSQL_TYPE_LONG: |
| case MYSQL_TYPE_LONGLONG: | case MYSQL_TYPE_LONGLONG: |
| case MYSQL_TYPE_INT24: | case MYSQL_TYPE_INT24: |
| #ifdef FIELD_TYPE_BIT | |
| case MYSQL_TYPE_BIT: | case MYSQL_TYPE_BIT: |
| #endif | |
| case MYSQL_TYPE_DATE: | case MYSQL_TYPE_DATE: |
| case MYSQL_TYPE_NEWDATE: | case MYSQL_TYPE_NEWDATE: |
| Line 103 inline static bool is_column_transcode_r | Line 94 inline static bool is_column_transcode_r |
| case MYSQL_TYPE_LONG_BLOB: | case MYSQL_TYPE_LONG_BLOB: |
| return false; | return false; |
| break; | 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); | char *strm=(char*)services.malloc_atomic(length+1); |
| memcpy(strm, str, length); | memcpy(strm, str, length); |
| strm[length]=0; | strm[length]=0; |
| return (const char*)strm; | return strm; |
| } | } |
| struct Connection { | struct Connection { |
| Line 131 public: | Line 123 public: |
| MySQL_Driver() : SQL_Driver() {} | MySQL_Driver() : SQL_Driver() {} |
| #ifndef FREEBSD4 | |
| ~MySQL_Driver() { | |
| if(mysql_server_end) | |
| mysql_server_end(); | |
| } | |
| #endif | |
| /// get api version | /// get api version |
| int api_version() { return SQL_DRIVER_API_VERSION; } | int api_version() { return SQL_DRIVER_API_VERSION; } |
| Line 150 public: | Line 135 public: |
| /** connect | /** connect |
| @param url | @param url |
| format: @b user:pass@host[:port]|[/unix/socket]/database? | 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 | ClientCharset=charset& // transcode by parser |
| timeout=3& | timeout=3& |
| compress=0& | compress=0& |
| named_pipe=1& | named_pipe=1& |
| autocommit=1& | autocommit=1& |
| multi_statements=0 // allows more then one statement in one query | multi_statements=0 // allows more then one statement in one query |
| old_client=1 // simulates 3.xx client. not compatible with multi_statements option | 4.1+ accept not 'cp1251_koi8' but 'cp1251', 'utf8' and much more |
| 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 | it is usable for transcoding using sql server |
| */ | */ |
| void connect( | 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 *user=url; |
| char *s=rsplit(user, '@'); | char *s=rsplit(user, '@'); |
| char *host=0; | char *host=0; |
| Line 181 public: | Line 159 public: |
| char *db=lsplit(s, '/'); | char *db=lsplit(s, '/'); |
| char *pwd=lsplit(user, ':'); | char *pwd=lsplit(user, ':'); |
| char *error_pos=0; | 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 *options=lsplit(db, '?'); |
| char *charset=0; | char *charset=0; |
| #ifdef OLD_MYSQL_CLIENT | |
| int client_flag=0; | |
| #else | |
| int client_flag=CLIENT_MULTI_RESULTS; | 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_ref=&connection; |
| connection.services=&services; | connection.services=&services; |
| connection.handle=mysql_init(NULL); | connection.handle=mysql_init(NULL); |
| connection.client_charset=0; | connection.client_charset=0; |
| connection.autocommit=true; | connection.autocommit=true; |
| while(options){ | while(1){ |
| if(char *key=lsplit(&options, '&')){ | char *next_host=lsplit(host, ','); |
| if(*key){ | 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(char *value=lsplit(key, '=')){ |
| if(strcmp(key, "ClientCharset")==0){ // transcoding with parser | if(strcmp(key, "ClientCharset")==0){ // transcoding with parser |
| toupper_str(value, value, strlen(value)); | toupper_str(value, value, strlen(value)); |
| Line 229 public: | Line 206 public: |
| if(atoi(value)==0) | if(atoi(value)==0) |
| connection.autocommit=false; | connection.autocommit=false; |
| } else if(strcasecmp(key, "multi_statements")==0){ | } 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) | if(atoi(value)!=0) |
| client_flag=CLIENT_MULTI_STATEMENTS; | client_flag=CLIENT_MULTI_STATEMENTS; |
| #endif | } else if(strcasecmp(key, "config_file")==0){ |
| } else if(strcasecmp(key, "old_client")==0){ | if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_FILE, value)!=0) |
| #ifdef OLD_MYSQL_CLIENT | services._throw(mysql_error(connection.handle)); |
| services._throw("driver was built with old mysql includes so old_client option can't be used"); | } else if(strcasecmp(key, "config_group")==0){ |
| #else | if(mysql_options(connection.handle, MYSQL_READ_DEFAULT_GROUP, value)!=0) |
| if(!(client_flag==CLIENT_MULTI_RESULTS || client_flag==0)) | services._throw(mysql_error(connection.handle)); |
| services._throw("you can't specify together options old_client and multi_statements"); | |
| if(atoi(value)!=0) | |
| client_flag=0; | |
| #endif | |
| } else | } else |
| services._throw("unknown connect option" /*key*/); | services._throw("unknown connect option" /*key*/); |
| } else | } else |
| services._throw("connect option without =value" /*key*/); | services._throw("connect option without =value" /*key*/); |
| } | } |
| } | } |
| } | |
| if(!mysql_real_connect( | if(mysql_real_connect(connection.handle, host, user, pwd, db, port, unix_socket, client_flag)) |
| connection.handle, | break; |
| host, user, pwd, db, | |
| port?port:MYSQL_PORT, | if(!next_host) |
| unix_socket, | services._throw(mysql_error(connection.handle)); |
| client_flag | |
| ) | host=next_host; |
| ){ | |
| services._throw(mysql_error(connection.handle)); | |
| } | } |
| if(charset){ | if(charset){ |
| char statement[MAX_STRING+1]="SET CHARACTER SET "; | char statement[MAX_STRING+1]="SET NAMES "; |
| strncat(statement, charset, MAX_STRING); | strncat(statement, charset, MAX_STRING); |
| _exec(connection, statement); | _exec(connection, statement); |
| } | } |
| Line 378 public: | Line 343 public: |
| if(transcode_needed) { | if(transcode_needed) { |
| statement_size=strlen(astatement); | statement_size=strlen(astatement); |
| // transcode query from $request:charset to ?ClientCharset | // transcode query from $request:charset to ?ClientCharset |
| services.transcode(astatement, statement_size, | services.transcode(astatement, statement_size, astatement, statement_size, services.request_charset(), connection.client_charset); |
| astatement, statement_size, | |
| services.request_charset(), | |
| connection.client_charset); | |
| } | } |
| const char *statement; | const char *statement; |
| Line 457 public: | Line 419 public: |
| } \ | } \ |
| } | } |
| bool* transcode_column=0; | |
| if(transcode_needed) { | if(transcode_needed) { |
| transcode_column = new bool[column_count]; | bool transcode_column[column_count]; |
| DO_FETCH_FIELDS( | DO_FETCH_FIELDS( |
| transcode_column[i] = is_column_transcode_required(field->type); | transcode_column[i] = is_column_transcode_required(field->type); |
| // transcode column's name from ?ClientCharset to $request:charset | // transcode column's name from ?ClientCharset to $request:charset |
| services.transcode(str, length, | services.transcode(str, length, str, length, connection.client_charset, services.request_charset()); |
| str, length, | |
| connection.client_charset, | |
| services.request_charset()); | |
| ) | ) |
| CHECK(handlers.before_rows(sql_error)); | CHECK(handlers.before_rows(sql_error)); |
| DO_FETCH_ROWS( | DO_FETCH_ROWS( |
| // transcode cell's value from ?ClientCharset to $request:charset | |
| if(transcode_column[i]) | 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()); |
| services.transcode(str, length, | |
| str, length, | |
| connection.client_charset, | |
| services.request_charset()); | |
| ) | ) |
| } else { | } else { |
| // without transcoding | // without transcoding |
| Line 484 public: | Line 439 public: |
| DO_FETCH_ROWS() | DO_FETCH_ROWS() |
| } | } |
| cleanup: | cleanup: |
| if(transcode_column) | |
| delete transcode_column; | |
| mysql_free_result(res); | mysql_free_result(res); |
| if(failed) | if(failed) |
| services._throw(sql_error); | services._throw(sql_error); |
| Line 501 private: | Line 454 private: |
| void _throw(Connection& connection, const char* aerr_msg) { | void _throw(Connection& connection, const char* aerr_msg) { |
| size_t length=strlen(aerr_msg); | size_t length=strlen(aerr_msg); |
| if(length && _transcode_required(connection)) { | if(length && _transcode_required(connection)) { |
| connection.services->transcode(aerr_msg, length, | connection.services->transcode(aerr_msg, length, aerr_msg, length, connection.client_charset, connection.services->request_charset()); |
| aerr_msg, length, | |
| connection.client_charset, | |
| connection.services->request_charset()); | |
| } | } |
| connection.services->_throw(aerr_msg); | connection.services->_throw(aerr_msg); |
| } | } |
| Line 515 private: | Line 465 private: |
| private: // mysql client library funcs | 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 void (STDCALL *t_mysql_server_end)(); t_mysql_server_end mysql_server_end; | 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 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 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; } | static char* STDCALL subst_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; } |
| typedef MYSQL* (STDCALL *t_mysql_real_connect)(MYSQL *, const char *host, | 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; |
| const char *user, | |
| const char *passwd, | typedef void (STDCALL *t_mysql_close)(MYSQL *); t_mysql_close mysql_close; |
| const char *db, | |
| unsigned int port, | typedef int (STDCALL *t_mysql_ping)(MYSQL *); t_mysql_ping mysql_ping; |
| const char *unix_socket, | |
| unsigned int clientflag); t_mysql_real_connect mysql_real_connect; | 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_close)(MYSQL *); t_mysql_close mysql_close; | typedef void (STDCALL *t_mysql_free_result)(MYSQL_RES *result); t_mysql_free_result mysql_free_result; |
| 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 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; } | typedef MYSQL_ROW (STDCALL *t_mysql_fetch_row)(MYSQL_RES *result); t_mysql_fetch_row mysql_fetch_row; |
| static unsigned int STDCALL subst_mysql_field_count(MYSQL *mysql) { return mysql->field_count; } | 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 | private: // mysql client library funcs linking |