diff options
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/Makefile.am | 17 | ||||
-rw-r--r-- | sql-common/client.c | 1366 | ||||
-rw-r--r-- | sql-common/client_plugin.c | 111 | ||||
-rw-r--r-- | sql-common/my_time.c | 22 | ||||
-rw-r--r-- | sql-common/my_user.c | 6 | ||||
-rw-r--r-- | sql-common/mysql_async.c | 1947 |
6 files changed, 2964 insertions, 505 deletions
diff --git a/sql-common/Makefile.am b/sql-common/Makefile.am deleted file mode 100644 index 2f5a049085f..00000000000 --- a/sql-common/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2003-2004, 2006 MySQL AB -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -## Process this file with automake to create Makefile.in -EXTRA_DIST = client.c pack.c my_time.c my_user.c client_plugin.c diff --git a/sql-common/client.c b/sql-common/client.c index ec294d2138d..349d844ebd3 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2003, 2010, Oracle and/or its affiliates. + Copyright (c) 2003, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file is included by both libmysql.c (the MySQL client C API) @@ -26,7 +25,6 @@ mysql_real_connect() - Support for reading local file with LOAD DATA LOCAL - SHARED memory handling - - Protection against sigpipe - Prepared statements - Things that only works for the server @@ -40,6 +38,10 @@ #include "mysql.h" +#ifndef __WIN__ +#include <netdb.h> +#endif + /* Remove client convenience wrappers */ #undef max_allowed_packet #undef net_buffer_length @@ -68,9 +70,9 @@ my_bool net_flush(NET *net); #include "mysqld_error.h" #include "errmsg.h" #include <violite.h> -#if defined(THREAD) && !defined(__WIN__) +#if !defined(__WIN__) #include <my_pthread.h> /* because of signal() */ -#endif /* defined(THREAD) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ #include <sys/stat.h> #include <signal.h> @@ -78,7 +80,7 @@ my_bool net_flush(NET *net); #ifdef HAVE_PWD_H #include <pwd.h> #endif -#if !defined(MSDOS) && !defined(__WIN__) +#if !defined(__WIN__) #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -89,12 +91,12 @@ my_bool net_flush(NET *net); #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#endif /*!defined(MSDOS) && !defined(__WIN__) */ +#endif /* !defined(__WIN__) */ #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif -#if defined(MSDOS) || defined(__WIN__) +#if defined(__WIN__) #define perror(A) #else #include <errno.h> @@ -110,6 +112,8 @@ my_bool net_flush(NET *net); #include "client_settings.h" #include <sql_common.h> #include <mysql/client_plugin.h> +#include <my_context.h> +#include <mysql_async.h> #define native_password_plugin_name "mysql_native_password" #define old_password_plugin_name "mysql_old_password" @@ -129,7 +133,7 @@ static void mysql_close_free(MYSQL *mysql); static void mysql_prune_stmt_list(MYSQL *mysql); static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length); -#if !(defined(__WIN__) || defined(__NETWARE__)) +#if !defined(__WIN__) static int wait_for_data(my_socket fd, uint timeout); #endif @@ -151,10 +155,13 @@ char mysql_server_last_error[MYSQL_ERRMSG_SIZE]; int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, uint timeout) { -#if defined(__WIN__) || defined(__NETWARE__) - return connect(fd, (struct sockaddr*) name, namelen); +#if defined(__WIN__) + DBUG_ENTER("my_connect"); + DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen)); #else int flags, res, s_err; + DBUG_ENTER("my_connect"); + DBUG_PRINT("enter", ("fd: %d timeout: %u", fd, timeout)); /* If they passed us a timeout of zero, we should behave @@ -162,24 +169,26 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, */ if (timeout == 0) - return connect(fd, (struct sockaddr*) name, namelen); + DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen)); flags = fcntl(fd, F_GETFL, 0); /* Set socket to not block */ #ifdef O_NONBLOCK fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */ #endif + DBUG_PRINT("info", ("connecting non-blocking")); res= connect(fd, (struct sockaddr*) name, namelen); + DBUG_PRINT("info", ("connect result: %d errno: %d", res, errno)); s_err= errno; /* Save the error... */ fcntl(fd, F_SETFL, flags); if ((res != 0) && (s_err != EINPROGRESS)) { errno= s_err; /* Restore it */ - return(-1); + DBUG_RETURN(-1); } if (res == 0) /* Connected quickly! */ - return(0); - return wait_for_data(fd, timeout); + DBUG_RETURN(0); + DBUG_RETURN(wait_for_data(fd, timeout)); #endif } @@ -191,33 +200,65 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen, If not, we will use select() */ -#if !(defined(__WIN__) || defined(__NETWARE__)) +#if !defined(__WIN__) static int wait_for_data(my_socket fd, uint timeout) { #ifdef HAVE_POLL struct pollfd ufds; int res; + DBUG_ENTER("wait_for_data"); + DBUG_PRINT("info", ("polling")); ufds.fd= fd; ufds.events= POLLIN | POLLPRI; if (!(res= poll(&ufds, 1, (int) timeout*1000))) { + DBUG_PRINT("info", ("poll timed out")); errno= EINTR; - return -1; + DBUG_RETURN(-1); } + DBUG_PRINT("info", + ("poll result: %d errno: %d revents: 0x%02d events: 0x%02d", + res, errno, ufds.revents, ufds.events)); if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI))) - return -1; - return 0; + DBUG_RETURN(-1); + /* + At this point, we know that something happened on the socket. + But this does not means that everything is alright. + The connect might have failed. We need to retrieve the error code + from the socket layer. We must return success only if we are sure + that it was really a success. Otherwise we might prevent the caller + from trying another address to connect to. + */ + { + int s_err; + socklen_t s_len= sizeof(s_err); + + DBUG_PRINT("info", ("Get SO_ERROR from non-blocked connected socket.")); + res= getsockopt(fd, SOL_SOCKET, SO_ERROR, &s_err, &s_len); + DBUG_PRINT("info", ("getsockopt res: %d s_err: %d", res, s_err)); + if (res) + DBUG_RETURN(res); + /* getsockopt() was successful, check the retrieved status value. */ + if (s_err) + { + errno= s_err; + DBUG_RETURN(-1); + } + /* Status from connect() is zero. Socket is successfully connected. */ + } + DBUG_RETURN(0); #else SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint); fd_set sfds; struct timeval tv; time_t start_time, now_time; int res, s_err; + DBUG_ENTER("wait_for_data"); if (fd >= FD_SETSIZE) /* Check if wrong error */ - return 0; /* Can't use timeout */ + DBUG_RETURN(0); /* Can't use timeout */ /* Our connection is "in progress." We can use the select() call to wait @@ -249,7 +290,7 @@ static int wait_for_data(my_socket fd, uint timeout) { tv.tv_sec = (long) timeout; tv.tv_usec = 0; -#if defined(HPUX10) && defined(THREAD) +#if defined(HPUX10) if ((res = select(fd+1, NULL, (int*) &sfds, NULL, &tv)) > 0) break; #else @@ -257,11 +298,11 @@ static int wait_for_data(my_socket fd, uint timeout) break; #endif if (res == 0) /* timeout */ - return -1; + DBUG_RETURN(-1); now_time= my_time(0); timeout-= (uint) (now_time - start_time); if (errno != EINTR || (int) timeout <= 0) - return -1; + DBUG_RETURN(-1); } /* @@ -272,17 +313,17 @@ static int wait_for_data(my_socket fd, uint timeout) s_err=0; if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) - return(-1); + DBUG_RETURN(-1); if (s_err) { /* getsockopt could succeed */ errno = s_err; - return(-1); /* but return an error... */ + DBUG_RETURN(-1); /* but return an error... */ } - return (0); /* ok */ + DBUG_RETURN(0); /* ok */ #endif /* HAVE_POLL */ } -#endif /* defined(__WIN__) || defined(__NETWARE__) */ +#endif /* !defined(__WIN__) */ /** Set the internal error message to mysql handler @@ -654,8 +695,7 @@ err2: CloseHandle(handle_file_map); } err: - if (tmp) - my_free(tmp, MYF(0)); + my_free(tmp); if (error_allow) error_code = GetLastError(); if (event_connect_request) @@ -680,24 +720,24 @@ err: } #endif -/***************************************************************************** +/** Read a packet from server. Give error message if socket was down or packet is an error message -*****************************************************************************/ + + @retval packet_error An error occurred during reading. + Error message is set. + @retval +*/ ulong cli_safe_read(MYSQL *mysql) { NET *net= &mysql->net; ulong len=0; - init_sigpipe_variables restart: - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); if (net->vio != 0) len=my_net_read(net); - reset_sigpipe(mysql); if (len == packet_error || len == 0) { @@ -779,7 +819,7 @@ void free_rows(MYSQL_DATA *cur) if (cur) { free_root(&cur->alloc,MYF(0)); - my_free((uchar*) cur,MYF(0)); + my_free(cur); } } @@ -791,13 +831,9 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, { NET *net= &mysql->net; my_bool result= 1; - init_sigpipe_variables my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_INIT_DONE : FALSE; DBUG_ENTER("cli_advanced_command"); - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); - if (mysql->net.vio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql) || stmt_skip) @@ -846,7 +882,6 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ? 1 : 0); end: - reset_sigpipe(mysql); DBUG_PRINT("exit",("result: %d", result)); DBUG_RETURN(result); } @@ -864,31 +899,132 @@ void free_old_query(MYSQL *mysql) DBUG_VOID_RETURN; } + +/** + Finish reading of a partial result set from the server. + Get the EOF packet, and update mysql->status + and mysql->warning_count. + + @return TRUE if a communication or protocol error, an error + is set in this case, FALSE otherwise. +*/ + +my_bool flush_one_result(MYSQL *mysql) +{ + ulong packet_length; + + DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY); + + do + { + packet_length= cli_safe_read(mysql); + /* + There is an error reading from the connection, + or (sic!) there were no error and no + data in the stream, i.e. no more data from the server. + Since we know our position in the stream (somewhere in + the middle of a result set), this latter case is an error too + -- each result set must end with a EOF packet. + cli_safe_read() has set an error for us, just return. + */ + if (packet_length == packet_error) + return TRUE; + } + while (packet_length > 8 || mysql->net.read_pos[0] != 254); + + /* Analyze EOF packet of the result set. */ + + if (protocol_41(mysql)) + { + char *pos= (char*) mysql->net.read_pos + 1; + mysql->warning_count=uint2korr(pos); + pos+=2; + mysql->server_status=uint2korr(pos); + pos+=2; + } + return FALSE; +} + + +/** + Read a packet from network. If it's an OK packet, flush it. + + @return TRUE if error, FALSE otherwise. In case of + success, is_ok_packet is set to TRUE or FALSE, + based on what we got from network. +*/ + +my_bool opt_flush_ok_packet(MYSQL *mysql, my_bool *is_ok_packet) +{ + ulong packet_length= cli_safe_read(mysql); + + if (packet_length == packet_error) + return TRUE; + + /* cli_safe_read always reads a non-empty packet. */ + DBUG_ASSERT(packet_length); + + *is_ok_packet= mysql->net.read_pos[0] == 0; + if (*is_ok_packet) + { + uchar *pos= mysql->net.read_pos + 1; + + net_field_length_ll(&pos); /* affected rows */ + net_field_length_ll(&pos); /* insert id */ + + mysql->server_status=uint2korr(pos); + pos+=2; + + if (protocol_41(mysql)) + { + mysql->warning_count=uint2korr(pos); + pos+=2; + } + } + return FALSE; +} + + /* Flush result set sent from server */ -static void cli_flush_use_result(MYSQL *mysql) +static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results) { /* Clear the current execution status */ DBUG_ENTER("cli_flush_use_result"); DBUG_PRINT("warning",("Not all packets read, clearing them")); - for (;;) + + if (flush_one_result(mysql)) + DBUG_VOID_RETURN; /* An error occurred */ + + if (! flush_all_results) + DBUG_VOID_RETURN; + + while (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { - ulong pkt_len; - if ((pkt_len=cli_safe_read(mysql)) == packet_error) - break; - if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) + my_bool is_ok_packet; + if (opt_flush_ok_packet(mysql, &is_ok_packet)) + DBUG_VOID_RETURN; /* An error occurred. */ + if (is_ok_packet) { - if (protocol_41(mysql)) - { - char *pos= (char*) mysql->net.read_pos + 1; - mysql->warning_count=uint2korr(pos); pos+=2; - mysql->server_status=uint2korr(pos); pos+=2; - } - break; /* End of data */ + /* + Indeed what we got from network was an OK packet, and we + know that OK is the last one in a multi-result-set, so + just return. + */ + DBUG_VOID_RETURN; } + /* + It's a result set, not an OK packet. A result set contains + of two result set subsequences: field metadata, terminated + with EOF packet, and result set data, again terminated with + EOF packet. Read and flush them. + */ + if (flush_one_result(mysql) || flush_one_result(mysql)) + DBUG_VOID_RETURN; /* An error occurred. */ } + DBUG_VOID_RETURN; } @@ -945,14 +1081,11 @@ void end_server(MYSQL *mysql) DBUG_ENTER("end_server"); if (mysql->net.vio != 0) { - init_sigpipe_variables DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio))); #ifdef MYSQL_SERVER slave_io_thread_detach_vio(); #endif - set_sigpipe(mysql); vio_delete(mysql->net.vio); - reset_sigpipe(mysql); mysql->net.vio= 0; /* Marker */ mysql_prune_stmt_list(mysql); } @@ -977,7 +1110,7 @@ mysql_free_result(MYSQL_RES *result) mysql->unbuffered_fetch_owner= 0; if (mysql->status == MYSQL_STATUS_USE_RESULT) { - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, FALSE); mysql->status=MYSQL_STATUS_READY; if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; @@ -986,9 +1119,8 @@ mysql_free_result(MYSQL_RES *result) free_rows(result->data); if (result->fields) free_root(&result->field_alloc,MYF(0)); - if (result->row) - my_free((uchar*) result->row,MYF(0)); - my_free((uchar*) result,MYF(0)); + my_free(result->row); + my_free(result); } DBUG_VOID_RETURN; } @@ -1004,7 +1136,6 @@ static const char *default_options[]= "ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath", "character-sets-dir", "default-character-set", "interactive-timeout", "connect-timeout", "local-infile", "disable-local-infile", - "replication-probe", "enable-reads-from-master", "repl-parse-query", "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", "report-data-truncation", "plugin-dir", "default-auth", @@ -1016,10 +1147,10 @@ enum option_id { OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath, OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout, OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile, - OPT_replication_probe, OPT_enable_reads_from_master, OPT_repl_parse_query, OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name, OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth, OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, + OPT_keep_this_one_last }; static TYPELIB option_types={array_elements(default_options)-1, @@ -1038,30 +1169,30 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd) { options->init_commands= (DYNAMIC_ARRAY*)my_malloc(sizeof(DYNAMIC_ARRAY), MYF(MY_WME)); - init_dynamic_array(options->init_commands,sizeof(char*),5,5 CALLER_INFO); + init_dynamic_array(options->init_commands,sizeof(char*),5,5); } if (!(tmp= my_strdup(cmd,MYF(MY_WME))) || insert_dynamic(options->init_commands, (uchar*)&tmp)) { - my_free(tmp, MYF(MY_ALLOW_ZERO_PTR)); + my_free(tmp); return 1; } return 0; } -#define extension_set(OPTS, X, VAL) \ +#define EXTENSION_SET(OPTS, X, VAL) \ if (!(OPTS)->extension) \ (OPTS)->extension= (struct st_mysql_options_extention *) \ my_malloc(sizeof(struct st_mysql_options_extention), \ MYF(MY_WME | MY_ZEROFILL)); \ (OPTS)->extension->X= VAL; -#define extension_set_string(OPTS, X, STR) \ +#define EXTENSION_SET_STRING(OPTS, X, STR) \ if ((OPTS)->extension) \ - my_free((OPTS)->extension->X, MYF(MY_ALLOW_ZERO_PTR)); \ - extension_set(OPTS, X, my_strdup((STR), MYF(MY_WME))); + my_free((OPTS)->extension->X); \ + EXTENSION_SET(OPTS, X, my_strdup((STR), MYF(MY_WME))); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group) @@ -1072,6 +1203,9 @@ void mysql_read_default_options(struct st_mysql_options *options, DBUG_ENTER("mysql_read_default_options"); DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); + compile_time_assert(OPT_keep_this_one_last == + array_elements(default_options)); + argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; groups[0]= (char*) "client"; groups[1]= (char*) "client-server"; @@ -1085,6 +1219,8 @@ void mysql_read_default_options(struct st_mysql_options *options, char **option=argv; while (*++option) { + if (my_getopt_is_args_separator(option[0])) /* skip arguments separator */ + continue; /* DBUG_PRINT("info",("option: %s",option[0])); */ if (option[0][0] == '-' && option[0][1] == '-') { @@ -1098,7 +1234,7 @@ void mysql_read_default_options(struct st_mysql_options *options, /* Change all '_' in variable name to '-' */ for (end= *option ; *(end= strcend(end,'_')) ; ) *end= '-'; - switch (find_type(*option+2,&option_types,2)) { + switch (find_type(*option + 2, &option_types, FIND_TYPE_BASIC)) { case OPT_port: if (opt_arg) options->port=atoi(opt_arg); @@ -1106,7 +1242,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_socket: if (opt_arg) { - my_free(options->unix_socket,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->unix_socket); options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1114,10 +1250,10 @@ void mysql_read_default_options(struct st_mysql_options *options, options->compress=1; options->client_flag|= CLIENT_COMPRESS; break; - case OPT_password: + case OPT_password: if (opt_arg) { - my_free(options->password,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->password); options->password=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1131,7 +1267,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_user: if (opt_arg) { - my_free(options->user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->user); options->user=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1141,14 +1277,14 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_host: if (opt_arg) { - my_free(options->host,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->host); options->host=my_strdup(opt_arg,MYF(MY_WME)); } break; case OPT_database: if (opt_arg) { - my_free(options->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->db); options->db=my_strdup(opt_arg,MYF(MY_WME)); } break; @@ -1160,25 +1296,25 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_return_found_rows: options->client_flag|=CLIENT_FOUND_ROWS; break; -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) case OPT_ssl_key: - my_free(options->ssl_key, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_key); options->ssl_key = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_cert: - my_free(options->ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_cert); options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_ca: - my_free(options->ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_ca); options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_capath: - my_free(options->ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_capath); options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_ssl_cipher: - my_free(options->ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->ssl_cipher); options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME)); break; #else @@ -1188,13 +1324,13 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_ssl_capath: case OPT_ssl_cipher: break; -#endif /* HAVE_OPENSSL */ - case OPT_character_sets_dir: - my_free(options->charset_dir,MYF(MY_ALLOW_ZERO_PTR)); +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ + case OPT_character_sets_dir: + my_free(options->charset_dir); options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_default_character_set: - my_free(options->charset_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->charset_name); options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_interactive_timeout: @@ -1209,26 +1345,13 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_disable_local_infile: options->client_flag&= ~CLIENT_LOCAL_FILES; break; - case OPT_replication_probe: -#ifndef TO_BE_DELETED - options->rpl_probe= 1; -#endif - break; - case OPT_enable_reads_from_master: - options->no_master_reads= 0; - break; - case OPT_repl_parse_query: -#ifndef TO_BE_DELETED - options->rpl_parse= 1; -#endif - break; case OPT_max_allowed_packet: if (opt_arg) options->max_allowed_packet= atoi(opt_arg); break; case OPT_protocol: - if ((options->protocol= find_type(opt_arg, - &sql_protocol_typelib,0)) <= 0) + if ((options->protocol= find_type(opt_arg, &sql_protocol_typelib, + FIND_TYPE_BASIC)) <= 0) { fprintf(stderr, "Unknown option to protocol: %s\n", opt_arg); exit(1); @@ -1237,7 +1360,7 @@ void mysql_read_default_options(struct st_mysql_options *options, case OPT_shared_memory_base_name: #ifdef HAVE_SMEM if (options->shared_memory_base_name != def_shared_memory_base_name) - my_free(options->shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(options->shared_memory_base_name); options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME)); #endif break; @@ -1255,10 +1378,22 @@ void mysql_read_default_options(struct st_mysql_options *options, options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; break; case OPT_plugin_dir: - extension_set_string(options, plugin_dir, opt_arg); + { + char buff[FN_REFLEN], buff2[FN_REFLEN]; + if (strlen(opt_arg) >= FN_REFLEN) + opt_arg[FN_REFLEN]= '\0'; + if (my_realpath(buff, opt_arg, 0)) + { + DBUG_PRINT("warning",("failed to normalize the plugin path: %s", + opt_arg)); + break; + } + convert_dirname(buff, buff2, NULL); + EXTENSION_SET_STRING(options, plugin_dir, buff2); + } break; case OPT_default_auth: - extension_set_string(options, default_auth, opt_arg); + EXTENSION_SET_STRING(options, default_auth, opt_arg); break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); @@ -1351,7 +1486,7 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, field->flags= uint2korr(pos+7); field->decimals= (uint) pos[9]; - if (INTERNAL_NUM_FIELD(field)) + if (IS_NUM(field->type)) field->flags|= NUM_FLAG; if (default_value && row->data[7]) { @@ -1392,7 +1527,7 @@ unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, field->flags= (uint) (uchar) row->data[4][0]; field->decimals=(uint) (uchar) row->data[4][1]; } - if (INTERNAL_NUM_FIELD(field)) + if (IS_NUM(field->type)) field->flags|= NUM_FLAG; if (default_value && row->data[5]) { @@ -1584,16 +1719,8 @@ mysql_init(MYSQL *mysql) else bzero((char*) (mysql), sizeof(*(mysql))); mysql->options.connect_timeout= CONNECT_TIMEOUT; - mysql->last_used_con= mysql->next_slave= mysql->master = mysql; mysql->charset=default_client_charset_info; strmov(mysql->net.sqlstate, not_error_sqlstate); - /* - By default, we are a replication pivot. The caller must reset it - after we return if this is not the case. - */ -#ifndef TO_BE_DELETED - mysql->rpl_pivot = 1; -#endif /* Only enable LOAD DATA INFILE by default if configured with @@ -1649,18 +1776,18 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , const char *cipher __attribute__((unused))) { DBUG_ENTER("mysql_ssl_set"); -#ifdef HAVE_OPENSSL - my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + my_free(mysql->options.ssl_key); + my_free(mysql->options.ssl_cert); + my_free(mysql->options.ssl_ca); + my_free(mysql->options.ssl_capath); + my_free(mysql->options.ssl_cipher); mysql->options.ssl_key= strdup_if_not_null(key); mysql->options.ssl_cert= strdup_if_not_null(cert); mysql->options.ssl_ca= strdup_if_not_null(ca); mysql->options.ssl_capath= strdup_if_not_null(capath); mysql->options.ssl_cipher= strdup_if_not_null(cipher); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_RETURN(0); } @@ -1670,7 +1797,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , NB! Errors are not reported until you do mysql_real_connect. */ -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) static void mysql_ssl_free(MYSQL *mysql __attribute__((unused))) @@ -1678,14 +1805,14 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; DBUG_ENTER("mysql_ssl_free"); - my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_key); + my_free(mysql->options.ssl_cert); + my_free(mysql->options.ssl_ca); + my_free(mysql->options.ssl_capath); + my_free(mysql->options.ssl_cipher); if (ssl_fd) SSL_CTX_free(ssl_fd->ssl_context); - my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->connector_fd); mysql->options.ssl_key = 0; mysql->options.ssl_cert = 0; mysql->options.ssl_ca = 0; @@ -1696,7 +1823,7 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) DBUG_VOID_RETURN; } -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ /* Return the SSL cipher (if any) used for current @@ -1712,10 +1839,10 @@ const char * STDCALL mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) { DBUG_ENTER("mysql_get_ssl_cipher"); -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) if (mysql->net.vio && mysql->net.vio->ssl_arg) DBUG_RETURN(SSL_get_cipher_name((SSL*)mysql->net.vio->ssl_arg)); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_RETURN(NULL); } @@ -1728,6 +1855,8 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) ssl_verify_server_cert() vio pointer to a SSL connected vio server_hostname name of the server that we connected to + errptr if we fail, we'll return (a pointer to a string + describing) the reason here RETURN VALUES 0 Success @@ -1735,9 +1864,9 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) */ -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) -static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) +static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const char **errptr) { SSL *ssl; X509 *server_cert; @@ -1748,19 +1877,19 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) if (!(ssl= (SSL*)vio->ssl_arg)) { - DBUG_PRINT("error", ("No SSL pointer found")); + *errptr= "No SSL pointer found"; DBUG_RETURN(1); } if (!server_hostname) { - DBUG_PRINT("error", ("No server hostname supplied")); + *errptr= "No server hostname supplied"; DBUG_RETURN(1); } if (!(server_cert= SSL_get_peer_certificate(ssl))) { - DBUG_PRINT("error", ("Could not get server certificate")); + *errptr= "Could not get server certificate"; DBUG_RETURN(1); } @@ -1789,7 +1918,7 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) DBUG_RETURN(0); } } - DBUG_PRINT("error", ("SSL certificate validation failure")); + *errptr= "SSL certificate validation failure"; DBUG_RETURN(1); } @@ -1831,52 +1960,299 @@ static MYSQL_METHODS client_methods= #endif }; + + +typedef enum my_cs_match_type_enum +{ + /* MySQL and OS charsets are fully compatible */ + my_cs_exact, + /* MySQL charset is very close to OS charset */ + my_cs_approx, + /* + MySQL knows this charset, but it is not supported as client character set. + */ + my_cs_unsupp +} my_cs_match_type; + + +typedef struct str2str_st +{ + const char *os_name; + const char *my_name; + my_cs_match_type param; +} MY_CSET_OS_NAME; + +const MY_CSET_OS_NAME charsets[]= +{ +#ifdef __WIN__ + {"cp437", "cp850", my_cs_approx}, + {"cp850", "cp850", my_cs_exact}, + {"cp852", "cp852", my_cs_exact}, + {"cp858", "cp850", my_cs_approx}, + {"cp866", "cp866", my_cs_exact}, + {"cp874", "tis620", my_cs_approx}, + {"cp932", "cp932", my_cs_exact}, + {"cp936", "gbk", my_cs_approx}, + {"cp949", "euckr", my_cs_approx}, + {"cp950", "big5", my_cs_exact}, + {"cp1200", "utf16le", my_cs_unsupp}, + {"cp1201", "utf16", my_cs_unsupp}, + {"cp1250", "cp1250", my_cs_exact}, + {"cp1251", "cp1251", my_cs_exact}, + {"cp1252", "latin1", my_cs_exact}, + {"cp1253", "greek", my_cs_exact}, + {"cp1254", "latin5", my_cs_exact}, + {"cp1255", "hebrew", my_cs_approx}, + {"cp1256", "cp1256", my_cs_exact}, + {"cp1257", "cp1257", my_cs_exact}, + {"cp10000", "macroman", my_cs_exact}, + {"cp10001", "sjis", my_cs_approx}, + {"cp10002", "big5", my_cs_approx}, + {"cp10008", "gb2312", my_cs_approx}, + {"cp10021", "tis620", my_cs_approx}, + {"cp10029", "macce", my_cs_exact}, + {"cp12001", "utf32", my_cs_unsupp}, + {"cp20107", "swe7", my_cs_exact}, + {"cp20127", "latin1", my_cs_approx}, + {"cp20866", "koi8r", my_cs_exact}, + {"cp20932", "ujis", my_cs_exact}, + {"cp20936", "gb2312", my_cs_approx}, + {"cp20949", "euckr", my_cs_approx}, + {"cp21866", "koi8u", my_cs_exact}, + {"cp28591", "latin1", my_cs_approx}, + {"cp28592", "latin2", my_cs_exact}, + {"cp28597", "greek", my_cs_exact}, + {"cp28598", "hebrew", my_cs_exact}, + {"cp28599", "latin5", my_cs_exact}, + {"cp28603", "latin7", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_4579_IS_DONE + {"cp28605", "latin9", my_cs_exact}, +#endif + {"cp38598", "hebrew", my_cs_exact}, + {"cp51932", "ujis", my_cs_exact}, + {"cp51936", "gb2312", my_cs_exact}, + {"cp51949", "euckr", my_cs_exact}, + {"cp51950", "big5", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_WL_4024_IS_DONE + {"cp54936", "gb18030", my_cs_exact}, +#endif + {"cp65001", "utf8", my_cs_exact}, + +#else /* not Windows */ + + {"646", "latin1", my_cs_approx}, /* Default on Solaris */ + {"ANSI_X3.4-1968", "latin1", my_cs_approx}, + {"ansi1251", "cp1251", my_cs_exact}, + {"armscii8", "armscii8", my_cs_exact}, + {"armscii-8", "armscii8", my_cs_exact}, + {"ASCII", "latin1", my_cs_approx}, + {"Big5", "big5", my_cs_exact}, + {"cp1251", "cp1251", my_cs_exact}, + {"cp1255", "hebrew", my_cs_approx}, + {"CP866", "cp866", my_cs_exact}, + {"eucCN", "gb2312", my_cs_exact}, + {"euc-CN", "gb2312", my_cs_exact}, + {"eucJP", "ujis", my_cs_exact}, + {"euc-JP", "ujis", my_cs_exact}, + {"eucKR", "euckr", my_cs_exact}, + {"euc-KR", "euckr", my_cs_exact}, +#ifdef UNCOMMENT_THIS_WHEN_WL_WL_4024_IS_DONE + {"gb18030", "gb18030", my_cs_exact}, +#endif + {"gb2312", "gb2312", my_cs_exact}, + {"gbk", "gbk", my_cs_exact}, + {"georgianps", "geostd8", my_cs_exact}, + {"georgian-ps", "geostd8", my_cs_exact}, + {"IBM-1252", "cp1252", my_cs_exact}, + + {"iso88591", "latin1", my_cs_approx}, + {"ISO_8859-1", "latin1", my_cs_approx}, + {"ISO8859-1", "latin1", my_cs_approx}, + {"ISO-8859-1", "latin1", my_cs_approx}, + + {"iso885913", "latin7", my_cs_exact}, + {"ISO_8859-13", "latin7", my_cs_exact}, + {"ISO8859-13", "latin7", my_cs_exact}, + {"ISO-8859-13", "latin7", my_cs_exact}, + +#ifdef UNCOMMENT_THIS_WHEN_WL_4579_IS_DONE + {"iso885915", "latin9", my_cs_exact}, + {"ISO_8859-15", "latin9", my_cs_exact}, + {"ISO8859-15", "latin9", my_cs_exact}, + {"ISO-8859-15", "latin9", my_cs_exact}, +#endif + + {"iso88592", "latin2", my_cs_exact}, + {"ISO_8859-2", "latin2", my_cs_exact}, + {"ISO8859-2", "latin2", my_cs_exact}, + {"ISO-8859-2", "latin2", my_cs_exact}, + + {"iso88597", "greek", my_cs_exact}, + {"ISO_8859-7", "greek", my_cs_exact}, + {"ISO8859-7", "greek", my_cs_exact}, + {"ISO-8859-7", "greek", my_cs_exact}, + + {"iso88598", "hebrew", my_cs_exact}, + {"ISO_8859-8", "hebrew", my_cs_exact}, + {"ISO8859-8", "hebrew", my_cs_exact}, + {"ISO-8859-8", "hebrew", my_cs_exact}, + + {"iso88599", "latin5", my_cs_exact}, + {"ISO_8859-9", "latin5", my_cs_exact}, + {"ISO8859-9", "latin5", my_cs_exact}, + {"ISO-8859-9", "latin5", my_cs_exact}, + + {"koi8r", "koi8r", my_cs_exact}, + {"KOI8-R", "koi8r", my_cs_exact}, + {"koi8u", "koi8u", my_cs_exact}, + {"KOI8-U", "koi8u", my_cs_exact}, + + {"roman8", "hp8", my_cs_exact}, /* Default on HP UX */ + + {"Shift_JIS", "sjis", my_cs_exact}, + {"SJIS", "sjis", my_cs_exact}, + {"shiftjisx0213", "sjis", my_cs_exact}, + + {"tis620", "tis620", my_cs_exact}, + {"tis-620", "tis620", my_cs_exact}, + + {"ujis", "ujis", my_cs_exact}, + + {"US-ASCII", "latin1", my_cs_approx}, + + {"utf8", "utf8", my_cs_exact}, + {"utf-8", "utf8", my_cs_exact}, +#endif + {NULL, NULL, 0} +}; + + +static const char * +my_os_charset_to_mysql_charset(const char *csname) +{ + const MY_CSET_OS_NAME *csp; + for (csp= charsets; csp->os_name; csp++) + { + if (!my_strcasecmp(&my_charset_latin1, csp->os_name, csname)) + { + switch (csp->param) + { + case my_cs_exact: + return csp->my_name; + + case my_cs_approx: + /* + Maybe we should print a warning eventually: + character set correspondence is not exact. + */ + return csp->my_name; + + default: + my_printf_error(ER_UNKNOWN_ERROR, + "OS character set '%s'" + " is not supported by MySQL client", + MYF(0), csp->my_name); + goto def; + } + } + } + + my_printf_error(ER_UNKNOWN_ERROR, + "Unknown OS character set '%s'.", + MYF(0), csname); + +def: + csname= MYSQL_DEFAULT_CHARSET_NAME; + my_printf_error(ER_UNKNOWN_ERROR, + "Switching to the default character set '%s'.", + MYF(0), csname); + return csname; +} + + +#ifndef __WIN__ +#include <stdlib.h> /* for getenv() */ +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#endif /* __WIN__ */ + + +static int +mysql_autodetect_character_set(MYSQL *mysql) +{ + const char *csname= MYSQL_DEFAULT_CHARSET_NAME; + +#ifdef __WIN__ + char cpbuf[64]; + { + my_snprintf(cpbuf, sizeof(cpbuf), "cp%d", (int) GetConsoleCP()); + csname= my_os_charset_to_mysql_charset(cpbuf); + } +#elif defined(HAVE_SETLOCALE) && defined(HAVE_NL_LANGINFO) + { + if (setlocale(LC_CTYPE, "") && (csname= nl_langinfo(CODESET))) + csname= my_os_charset_to_mysql_charset(csname); + } +#endif + + my_free(mysql->options.charset_name); + if (!(mysql->options.charset_name= my_strdup(csname, MYF(MY_WME)))) + return 1; + return 0; +} + + +static void +mysql_set_character_set_with_default_collation(MYSQL *mysql) +{ + const char *save= charsets_dir; + if (mysql->options.charset_dir) + charsets_dir=mysql->options.charset_dir; + + if ((mysql->charset= get_charset_by_csname(mysql->options.charset_name, + MY_CS_PRIMARY, MYF(MY_WME)))) + { + /* Try to set compiled default collation when it's possible. */ + CHARSET_INFO *collation; + if ((collation= + get_charset_by_name(MYSQL_DEFAULT_COLLATION_NAME, MYF(MY_WME))) && + my_charset_same(mysql->charset, collation)) + { + mysql->charset= collation; + } + else + { + /* + Default compiled collation not found, or is not applicable + to the requested character set. + Continue with the default collation of the character set. + */ + } + } + charsets_dir= save; +} + + C_MODE_START int mysql_init_character_set(MYSQL *mysql) { - const char *default_collation_name; - /* Set character set */ if (!mysql->options.charset_name) { - default_collation_name= MYSQL_DEFAULT_COLLATION_NAME; if (!(mysql->options.charset_name= my_strdup(MYSQL_DEFAULT_CHARSET_NAME,MYF(MY_WME)))) - return 1; - } - else - default_collation_name= NULL; - - { - const char *save= charsets_dir; - if (mysql->options.charset_dir) - charsets_dir=mysql->options.charset_dir; - mysql->charset=get_charset_by_csname(mysql->options.charset_name, - MY_CS_PRIMARY, MYF(MY_WME)); - if (mysql->charset && default_collation_name) - { - CHARSET_INFO *collation; - if ((collation= - get_charset_by_name(default_collation_name, MYF(MY_WME)))) - { - if (!my_charset_same(mysql->charset, collation)) - { - my_printf_error(ER_UNKNOWN_ERROR, - "COLLATION %s is not valid for CHARACTER SET %s", - MYF(0), - default_collation_name, mysql->options.charset_name); - mysql->charset= NULL; - } - else - { - mysql->charset= collation; - } - } - else - mysql->charset= NULL; - } - charsets_dir= save; + return 1; } + else if (!strcmp(mysql->options.charset_name, + MYSQL_AUTODETECT_CHARSET_NAME) && + mysql_autodetect_character_set(mysql)) + return 1; + + mysql_set_character_set_with_default_collation(mysql); if (!mysql->charset) { @@ -1915,6 +2291,9 @@ static auth_plugin_t native_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Native MySQL authentication", {1, 0, 0}, + "GPL", + NULL, + NULL, NULL, NULL, native_password_auth_client @@ -1928,6 +2307,9 @@ static auth_plugin_t old_password_client_plugin= "R.J.Silk, Sergei Golubchik", "Old MySQL-3.23 authentication", {1, 0, 0}, + "GPL", + NULL, + NULL, NULL, NULL, old_password_auth_client @@ -1940,8 +2322,6 @@ struct st_mysql_client_plugin *mysql_client_builtins[]= 0 }; - - /* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */ typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); @@ -1949,15 +2329,15 @@ typedef struct { void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); /* -= end of MYSQL_PLUGIN_VIO =- */ MYSQL *mysql; - auth_plugin_t *plugin; /**< what plugin we're under */ + auth_plugin_t *plugin; /**< what plugin we're under */ const char *db; struct { - uchar *pkt; /**< pointer into NET::buff */ + uchar *pkt; /**< pointer into NET::buff */ uint pkt_len; } cached_server_reply; - uint packets_read, packets_written; /**< counters for send/received packets */ - my_bool mysql_change_user; /**< if it's mysql_change_user() */ - int last_read_packet_len; /**< the length of the last *read* packet */ + int packets_read, packets_written; /**< counters for send/received packets */ + int mysql_change_user; /**< if it's mysql_change_user() */ + int last_read_packet_len; /**< the length of the last *read* packet */ } MCPVIO_EXT; /** @@ -1979,7 +2359,6 @@ typedef struct { @retval 0 ok @retval 1 error */ - static int send_change_user_packet(MCPVIO_EXT *mpvio, const uchar *data, int data_len) { @@ -2063,7 +2442,6 @@ error: @retval 0 ok @retval 1 error */ - static int send_client_reply_packet(MCPVIO_EXT *mpvio, const uchar *data, int data_len) { @@ -2121,7 +2499,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, /* Do the SSL layering. */ struct st_mysql_options *options= &mysql->options; struct st_VioSSLFd *ssl_fd; - char error_string[1024]; + enum enum_ssl_init_error ssl_init_error; + const char *cert_error; + unsigned long ssl_error; /* Send mysql->client_flag, max_packet_size - unencrypted otherwise @@ -2141,32 +2521,36 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, options->ssl_cert, options->ssl_ca, options->ssl_capath, - options->ssl_cipher))) + options->ssl_cipher, + &ssl_init_error))) { - set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), sslGetErrString(ssl_init_error)); goto error; } - mysql->connector_fd= (void*)ssl_fd; + mysql->connector_fd= (unsigned char *) ssl_fd; /* Connect to the server */ DBUG_PRINT("info", ("IO layer change in progress...")); if (sslconnect(ssl_fd, net->vio, - (long) (mysql->options.connect_timeout), - error_string)) - { - set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, - unknown_sqlstate, "SSL error: %s", - error_string[0] ? error_string : - ER(CR_SSL_CONNECTION_ERROR)); + (long) (mysql->options.connect_timeout), &ssl_error)) + { + char buf[512]; + ERR_error_string_n(ssl_error, buf, 512); + buf[511]= 0; + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), + buf); goto error; } DBUG_PRINT("info", ("IO layer change done!")); /* Verify server cert */ if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && - ssl_verify_server_cert(net->vio, mysql->host)) + ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { - set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), cert_error); goto error; } } @@ -2239,7 +2623,6 @@ error: This function is called by a client authentication plugin, when it wants to read data from the server. */ - static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) { MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; @@ -2302,7 +2685,6 @@ static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) It transparently wraps the data into a change user or authentication handshake packet, if neccessary. */ - static int client_mpvio_write_packet(struct st_plugin_vio *mpv, const uchar *pkt, int pkt_len) { @@ -2338,7 +2720,6 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv, fills MYSQL_PLUGIN_VIO_INFO structure with the information about the connection */ - void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) { bzero(info, sizeof(*info)); @@ -2369,7 +2750,9 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) return; case VIO_TYPE_SHARED_MEMORY: info->protocol= MYSQL_VIO_MEMORY; +#ifdef HAVE_SMEM info->handle= vio->handle_file_map; /* or what ? */ +#endif return; #endif default: DBUG_ASSERT(0); @@ -2401,7 +2784,6 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, @retval 0 ok @retval 1 error */ - int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *data_plugin, const char *db) { @@ -2411,6 +2793,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ulong pkt_length; int res; + DBUG_ENTER ("run_plugin_auth"); /* determine the default/initial plugin to use */ if (mysql->options.extension && mysql->options.extension->default_auth && mysql->server_capabilities & CLIENT_PLUGIN_AUTH) @@ -2418,7 +2801,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, auth_plugin_name= mysql->options.extension->default_auth; if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) - return 1; /* oops, not found */ + DBUG_RETURN (1); /* oops, not found */ } else { @@ -2427,6 +2810,8 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, auth_plugin_name= auth_plugin->name; } + DBUG_PRINT ("info", ("using plugin %s", auth_plugin_name)); + mysql->net.last_errno= 0; /* just in case */ if (data_plugin && strcmp(data_plugin, auth_plugin_name)) @@ -2448,6 +2833,11 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, mpvio.plugin= auth_plugin; res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + DBUG_PRINT ("info", ("authenticate_user returned %s", + res == CR_OK ? "CR_OK" : + res == CR_ERROR ? "CR_ERROR" : + res == CR_OK_HANDSHAKE_COMPLETE ? + "CR_OK_HANDSHAKE_COMPLETE" : "error")); compile_time_assert(CR_OK == -1); compile_time_assert(CR_ERROR == 0); @@ -2458,12 +2848,13 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, unless the error code is CR_ERROR and mysql->net.last_errno is already set (the plugin has done it) */ + DBUG_PRINT ("info", ("res=%d", res)); if (res > CR_ERROR) set_mysql_error(mysql, res, unknown_sqlstate); else if (!mysql->net.last_errno) set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); - return 1; + DBUG_RETURN (1); } /* read the OK packet (or use the cached value in mysql->net.read_pos */ @@ -2472,6 +2863,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, else /* res == CR_OK_HANDSHAKE_COMPLETE */ pkt_length= mpvio.last_read_packet_len; + DBUG_PRINT ("info", ("OK packet length=%lu", pkt_length)); if (pkt_length == packet_error) { if (mysql->net.last_errno == CR_SERVER_LOST) @@ -2479,7 +2871,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ER(CR_SERVER_LOST_EXTENDED), "reading authorization packet", errno); - return 1; + DBUG_RETURN (1); } if (mysql->net.read_pos[0] == 254) @@ -2488,6 +2880,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, if (pkt_length == 1) { /* old "use short scramble" packet */ + DBUG_PRINT ("info", ("old use short scramble packet from server")); auth_plugin_name= old_password_plugin_name; mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble; mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; @@ -2500,15 +2893,22 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */ mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; + DBUG_PRINT ("info", ("change plugin packet from server for plugin %s", + auth_plugin_name)); } if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) - return 1; + DBUG_RETURN (1); mpvio.plugin= auth_plugin; res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); + DBUG_PRINT ("info", ("second authenticate_user returned %s", + res == CR_OK ? "CR_OK" : + res == CR_ERROR ? "CR_ERROR" : + res == CR_OK_HANDSHAKE_COMPLETE ? + "CR_OK_HANDSHAKE_COMPLETE" : "error")); if (res > CR_OK) { if (res > CR_ERROR) @@ -2516,7 +2916,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, else if (!mysql->net.last_errno) set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); - return 1; + DBUG_RETURN (1); } if (res != CR_OK_HANDSHAKE_COMPLETE) @@ -2529,7 +2929,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, ER(CR_SERVER_LOST_EXTENDED), "reading final connect information", errno); - return 1; + DBUG_RETURN (1); } } } @@ -2537,46 +2937,59 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, net->read_pos[0] should always be 0 here if the server implements the protocol correctly */ - return mysql->net.read_pos[0] != 0; + DBUG_RETURN (mysql->net.read_pos[0] != 0); } +static int +connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, + const struct sockaddr *name, uint namelen) +{ + if (mysql->options.extension && mysql->options.extension->async_context && + mysql->options.extension->async_context->active) + { + my_bool old_mode; + vio_blocking(net->vio, FALSE, &old_mode); + return my_connect_async(mysql->options.extension->async_context, fd, + name, namelen, mysql->options.connect_timeout); + } + + return my_connect(fd, name, namelen, mysql->options.connect_timeout); +} + MYSQL * STDCALL CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket,ulong client_flag) { char buff[NAME_LEN+USERNAME_LENGTH+100]; - int scramble_data_len, pkt_scramble_len; - char *end, *host_info=0, *server_version_end, *pkt_end; + int scramble_data_len, pkt_scramble_len= 0; + char *end,*host_info= 0, *server_version_end, *pkt_end; char *scramble_data; const char *scramble_plugin; - my_socket sock; - in_addr_t ip_addr; - struct sockaddr_in sock_addr; ulong pkt_length; NET *net= &mysql->net; -#ifdef MYSQL_SERVER - thr_alarm_t alarmed; - ALARM alarm_buff; -#endif #ifdef __WIN__ HANDLE hPipe=INVALID_HANDLE_VALUE; #endif #ifdef HAVE_SYS_UN_H struct sockaddr_un UNIXaddr; #endif - init_sigpipe_variables DBUG_ENTER("mysql_real_connect"); LINT_INIT(pkt_scramble_len); - DBUG_PRINT("enter",("host: %s db: %s user: %s", + DBUG_PRINT("enter",("host: %s db: %s user: %s (client)", host ? host : "(Null)", db ? db : "(Null)", user ? user : "(Null)")); - /* Don't give sigpipe errors if the client doesn't want them */ - set_sigpipe(mysql); + /* Test whether we're already connected */ + if (net->vio) + { + set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate); + DBUG_RETURN(0); + } + mysql->methods= &client_methods; net->vio = 0; /* If something goes wrong */ mysql->client_flag=0; /* For handshake */ @@ -2588,8 +3001,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (mysql->options.my_cnf_file ? mysql->options.my_cnf_file : "my"), mysql->options.my_cnf_group); - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; } @@ -2620,6 +3033,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, unix_socket=mysql->options.unix_socket; mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + DBUG_PRINT("info", ("Connecting")); /* Part 0: Grab a socket and connect it to the server @@ -2630,6 +3044,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (!host || !strcmp(host,LOCAL_HOST)) && mysql->options.shared_memory_base_name) { + DBUG_PRINT("info", ("Using shared memory")); if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) == INVALID_HANDLE_VALUE) { @@ -2652,7 +3067,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, else { mysql->options.protocol=MYSQL_PROTOCOL_MEMORY; - sock=0; unix_socket = 0; host=mysql->options.shared_memory_base_name; my_snprintf(host_info=buff, sizeof(buff)-1, @@ -2667,12 +3081,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (unix_socket || mysql_unix_port) && (!host || !strcmp(host,LOCAL_HOST))) { - host=LOCAL_HOST; - if (!unix_socket) - unix_socket=mysql_unix_port; - host_info=(char*) ER(CR_LOCALHOST_CONNECTION); - DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket)); - if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) + my_socket sock= socket(AF_UNIX, SOCK_STREAM, 0); + DBUG_PRINT("info", ("Using socket")); + if (sock == SOCKET_ERROR) { set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR, unknown_sqlstate, @@ -2680,13 +3091,28 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, socket_errno); goto error; } + net->vio= vio_new(sock, VIO_TYPE_SOCKET, VIO_LOCALHOST | VIO_BUFFERED_READ); - bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); - UNIXaddr.sun_family = AF_UNIX; + if (!net->vio) + { + DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol)); + set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate); + closesocket(sock); + goto error; + } + + host= LOCAL_HOST; + if (!unix_socket) + unix_socket= mysql_unix_port; + host_info= (char*) ER(CR_LOCALHOST_CONNECTION); + DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket)); + + bzero((char*) &UNIXaddr, sizeof(UNIXaddr)); + UNIXaddr.sun_family= AF_UNIX; strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1); - if (my_connect(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr), - mysql->options.connect_timeout)) + if (connect_sync_or_async(mysql, net, sock, + (struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr))) { DBUG_PRINT("error",("Got error %d on connect to local server", socket_errno)); @@ -2694,6 +3120,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, unknown_sqlstate, ER(CR_CONNECTION_ERROR), unix_socket, socket_errno); + vio_delete(net->vio); + net->vio= 0; goto error; } mysql->options.protocol=MYSQL_PROTOCOL_SOCKET; @@ -2704,7 +3132,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || (! have_tcpip && (unix_socket || !host && is_NT())))) { - sock=0; if ((hPipe= create_named_pipe(mysql, mysql->options.connect_timeout, (char**) &host, (char**) &unix_socket)) == INVALID_HANDLE_VALUE) @@ -2728,93 +3155,122 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } } #endif + DBUG_PRINT("info", ("net->vio: %p protocol: %d", + net->vio, mysql->options.protocol)); if (!net->vio && (!mysql->options.protocol || mysql->options.protocol == MYSQL_PROTOCOL_TCP)) { - int status= -1; + struct addrinfo *res_lst, hints, *t_res; + int gai_errno; + char port_buf[NI_MAXSERV]; + my_socket sock= SOCKET_ERROR; + int saved_error= 0, status= -1; + unix_socket=0; /* This is not used */ + if (!port) - port=mysql_port; + port= mysql_port; + if (!host) - host=LOCAL_HOST; - my_snprintf(host_info=buff,sizeof(buff)-1,ER(CR_TCP_CONNECTION),host); - DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); -#ifdef MYSQL_SERVER - thr_alarm_init(&alarmed); - thr_alarm(&alarmed, mysql->options.connect_timeout, &alarm_buff); -#endif - /* _WIN64 ; Assume that the (int) range is enough for socket() */ - sock = (my_socket) socket(AF_INET,SOCK_STREAM,0); -#ifdef MYSQL_SERVER - thr_end_alarm(&alarmed); -#endif - if (sock == SOCKET_ERROR) - { - set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, - ER(CR_IPSOCK_ERROR), socket_errno); + host= LOCAL_HOST; + + my_snprintf(host_info=buff, sizeof(buff)-1, ER(CR_TCP_CONNECTION), host); + DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host, port)); + DBUG_PRINT("info",("IP '%s'", "client")); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype= SOCK_STREAM; + hints.ai_protocol= IPPROTO_TCP; + hints.ai_family= AF_UNSPEC; + + DBUG_PRINT("info",("IPV6 getaddrinfo %s", host)); + my_snprintf(port_buf, NI_MAXSERV, "%d", port); + gai_errno= getaddrinfo(host, port_buf, &hints, &res_lst); + + if (gai_errno != 0) + { + /* + For DBUG we are keeping the right message but for client we default to + historical error message. + */ + DBUG_PRINT("info",("IPV6 getaddrinfo error %d", gai_errno)); + set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, + ER(CR_UNKNOWN_HOST), host, errno); + goto error; } - net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); - bzero((char*) &sock_addr,sizeof(sock_addr)); - sock_addr.sin_family = AF_INET; - sock_addr.sin_port = (ushort) htons((ushort) port); /* - The server name may be a host name or IP address + A hostname might map to multiple IP addresses (IPv4/IPv6). Go over the + list of IP addresses until a successful connection can be established. */ - - if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) - { - memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); - status= my_connect(sock, (struct sockaddr *) &sock_addr, - sizeof(sock_addr), mysql->options.connect_timeout); - } - else + DBUG_PRINT("info", ("Try connect on all addresses for host.")); + for (t_res= res_lst; t_res; t_res= t_res->ai_next) { - int i, tmp_errno; - struct hostent tmp_hostent,*hp; - char buff2[GETHOSTBYNAME_BUFF_SIZE]; - hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), - &tmp_errno); - - /* - Don't attempt to connect to non IPv4 addresses as the client could - end up sending information to a unknown server. For example, a IPv6 - address might be returned from gethostbyname depending on options - set via the RES_OPTIONS environment variable. - */ - if (!hp || (hp->h_addrtype != AF_INET)) + DBUG_PRINT("info", ("Create socket, family: %d type: %d proto: %d", + t_res->ai_family, t_res->ai_socktype, + t_res->ai_protocol)); + sock= socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol); + if (sock == SOCKET_ERROR) { - my_gethostbyname_r_free(); - set_mysql_extended_error(mysql, CR_UNKNOWN_HOST, unknown_sqlstate, - ER(CR_UNKNOWN_HOST), host, tmp_errno); - goto error; + saved_error= socket_errno; + continue; } - for (i= 0; status && hp->h_addr_list[i]; i++) + net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); + if (!net->vio) { - IF_DBUG(char ipaddr[18];) - memcpy(&sock_addr.sin_addr, hp->h_addr_list[i], - min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length)); - DBUG_PRINT("info",("Trying %s...", - (my_inet_ntoa(sock_addr.sin_addr, ipaddr), ipaddr))); - status= my_connect(sock, (struct sockaddr *) &sock_addr, - sizeof(sock_addr), mysql->options.connect_timeout); + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + closesocket(sock); + freeaddrinfo(res_lst); + goto error; } - my_gethostbyname_r_free(); + DBUG_PRINT("info", ("Connect socket")); + status= connect_sync_or_async(mysql, net, sock, + t_res->ai_addr, t_res->ai_addrlen); + /* + Here we rely on my_connect() to return success only if the + connect attempt was really successful. Otherwise we would stop + trying another address, believing we were successful. + */ + if (!status) + break; + + /* + Save value as socket errno might be overwritten due to + calling a socket function below. + */ + saved_error= socket_errno; + + DBUG_PRINT("info", ("No success, close socket, try next address.")); + vio_delete(mysql->net.vio); + mysql->net.vio= 0; + } + DBUG_PRINT("info", + ("End of connect attempts, sock: %d status: %d error: %d", + sock, status, saved_error)); + + freeaddrinfo(res_lst); + + if (sock == SOCKET_ERROR) + { + set_mysql_extended_error(mysql, CR_IPSOCK_ERROR, unknown_sqlstate, + ER(CR_IPSOCK_ERROR), saved_error); + goto error; } if (status) { - DBUG_PRINT("error",("Got error %d on connect to '%s'",socket_errno, - host)); + DBUG_PRINT("error",("Got error %d on connect to '%s'", saved_error, host)); set_mysql_extended_error(mysql, CR_CONN_HOST_ERROR, unknown_sqlstate, - ER(CR_CONN_HOST_ERROR), host, socket_errno); + ER(CR_CONN_HOST_ERROR), host, saved_error); goto error; } } + + DBUG_PRINT("info", ("net->vio: %p", net->vio)); if (!net->vio) { DBUG_PRINT("error",("Unknow protocol %d ",mysql->options.protocol)); @@ -2822,6 +3278,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, goto error; } + if (mysql->options.extension && mysql->options.extension->async_context) + net->vio->async_context= mysql->options.extension->async_context; + if (my_net_init(net, net->vio)) { vio_delete(net->vio); @@ -2857,6 +3316,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, /* Part 1: Connection established, read and parse first packet */ + DBUG_PRINT("info", ("Read first packet.")); if ((pkt_length=cli_safe_read(mysql)) == packet_error) { @@ -3028,17 +3488,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, } #endif -#ifndef TO_BE_DELETED - if (mysql->options.rpl_probe && mysql_rpl_probe(mysql)) - goto error; -#endif - DBUG_PRINT("exit", ("Mysql handler: 0x%lx", (long) mysql)); - reset_sigpipe(mysql); DBUG_RETURN(mysql); error: - reset_sigpipe(mysql); DBUG_PRINT("error",("message: %u/%s (%s)", net->last_errno, net->sqlstate, @@ -3047,38 +3500,39 @@ error: /* Free alloced memory */ end_server(mysql); mysql_close_free(mysql); - if (!(client_flag & CLIENT_REMEMBER_OPTIONS)) - mysql_close_free_options(mysql); } DBUG_RETURN(0); } -/* needed when we move MYSQL structure to a different address */ - -#ifndef TO_BE_DELETED -static void mysql_fix_pointers(MYSQL* mysql, MYSQL* old_mysql) +struct my_hook_data { + MYSQL *orig_mysql; + MYSQL *new_mysql; + /* This is always NULL currently, but restoring does not hurt just in case. */ + Vio *orig_vio; +}; +/* + Callback hook to make the new VIO accessible via the old MYSQL to calling + application when suspending a non-blocking call during automatic reconnect. +*/ +static void +my_suspend_hook(my_bool suspend, void *data) { - MYSQL *tmp, *tmp_prev; - if (mysql->master == old_mysql) - mysql->master= mysql; - if (mysql->last_used_con == old_mysql) - mysql->last_used_con= mysql; - if (mysql->last_used_slave == old_mysql) - mysql->last_used_slave= mysql; - for (tmp_prev = mysql, tmp = mysql->next_slave; - tmp != old_mysql;tmp = tmp->next_slave) + struct my_hook_data *hook_data= (struct my_hook_data *)data; + if (suspend) { - tmp_prev= tmp; + hook_data->orig_vio= hook_data->orig_mysql->net.vio; + hook_data->orig_mysql->net.vio= hook_data->new_mysql->net.vio; } - tmp_prev->next_slave= mysql; + else + hook_data->orig_mysql->net.vio= hook_data->orig_vio; } -#endif - my_bool mysql_reconnect(MYSQL *mysql) { MYSQL tmp_mysql; + struct my_hook_data hook_data; + struct mysql_async_context *ctxt= NULL; DBUG_ENTER("mysql_reconnect"); DBUG_ASSERT(mysql); DBUG_PRINT("enter", ("mysql->reconnect: %d", mysql->reconnect)); @@ -3094,12 +3548,32 @@ my_bool mysql_reconnect(MYSQL *mysql) mysql_init(&tmp_mysql); tmp_mysql.options= mysql->options; tmp_mysql.options.my_cnf_file= tmp_mysql.options.my_cnf_group= 0; - tmp_mysql.rpl_pivot= mysql->rpl_pivot; - + + /* + If we are automatically re-connecting inside a non-blocking API call, we + may need to suspend and yield to the user application during the reconnect. + If so, the user application will need access to the new VIO already then + so that it can correctly wait for I/O to become ready. + To achieve this, we temporarily install a hook that will temporarily put in + the VIO while we are suspended. + (The vio will be put in the original MYSQL permanently once we successfully + reconnect, or be discarded if we fail to reconnect.) + */ + if (mysql->options.extension && + (ctxt= mysql->options.extension->async_context) && + mysql->options.extension->async_context->active) + { + hook_data.orig_mysql= mysql; + hook_data.new_mysql= &tmp_mysql; + hook_data.orig_vio= mysql->net.vio; + my_context_install_suspend_resume_hook(ctxt, my_suspend_hook, &hook_data); + } if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, - mysql->client_flag | CLIENT_REMEMBER_OPTIONS)) + mysql->client_flag)) { + if (ctxt) + my_context_install_suspend_resume_hook(ctxt, NULL, NULL); mysql->net.last_errno= tmp_mysql.net.last_errno; strmov(mysql->net.last_error, tmp_mysql.net.last_error); strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); @@ -3110,11 +3584,15 @@ my_bool mysql_reconnect(MYSQL *mysql) DBUG_PRINT("error", ("mysql_set_character_set() failed")); bzero((char*) &tmp_mysql.options,sizeof(tmp_mysql.options)); mysql_close(&tmp_mysql); + if (ctxt) + my_context_install_suspend_resume_hook(ctxt, NULL, NULL); mysql->net.last_errno= tmp_mysql.net.last_errno; strmov(mysql->net.last_error, tmp_mysql.net.last_error); strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); DBUG_RETURN(1); } + if (ctxt) + my_context_install_suspend_resume_hook(ctxt, NULL, NULL); DBUG_PRINT("info", ("reconnect succeded")); tmp_mysql.reconnect= 1; @@ -3129,7 +3607,6 @@ my_bool mysql_reconnect(MYSQL *mysql) mysql->free_me=0; mysql_close(mysql); *mysql=tmp_mysql; - mysql_fix_pointers(mysql, &tmp_mysql); /* adjust connection pointers */ net_clear(&mysql->net, 1); mysql->affected_rows= ~(my_ulonglong) 0; DBUG_RETURN(0); @@ -3150,7 +3627,7 @@ mysql_select_db(MYSQL *mysql, const char *db) if ((error=simple_command(mysql,COM_INIT_DB, (const uchar*) db, (ulong) strlen(db),0))) DBUG_RETURN(error); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->db); mysql->db=my_strdup(db,MYF(MY_WME)); DBUG_RETURN(0); } @@ -3165,38 +3642,44 @@ static void mysql_close_free_options(MYSQL *mysql) { DBUG_ENTER("mysql_close_free_options"); - my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.client_ip,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.user); + my_free(mysql->options.host); + my_free(mysql->options.password); + my_free(mysql->options.unix_socket); + my_free(mysql->options.db); + my_free(mysql->options.my_cnf_file); + my_free(mysql->options.my_cnf_group); + my_free(mysql->options.charset_dir); + my_free(mysql->options.charset_name); + my_free(mysql->options.client_ip); if (mysql->options.init_commands) { DYNAMIC_ARRAY *init_commands= mysql->options.init_commands; char **ptr= (char**)init_commands->buffer; char **end= ptr + init_commands->elements; for (; ptr<end; ptr++) - my_free(*ptr,MYF(MY_WME)); + my_free(*ptr); delete_dynamic(init_commands); - my_free((char*)init_commands,MYF(MY_WME)); + my_free(init_commands); } -#ifdef HAVE_OPENSSL +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) mysql_ssl_free(mysql); -#endif /* HAVE_OPENSSL */ +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #ifdef HAVE_SMEM if (mysql->options.shared_memory_base_name != def_shared_memory_base_name) - my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.shared_memory_base_name); #endif /* HAVE_SMEM */ if (mysql->options.extension) { - my_free(mysql->options.extension->plugin_dir,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.extension->default_auth,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.extension,MYF(0)); + struct mysql_async_context *ctxt= mysql->options.extension->async_context; + my_free(mysql->options.extension->plugin_dir); + my_free(mysql->options.extension->default_auth); + if (ctxt) + { + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + } + my_free(mysql->options.extension); } bzero((char*) &mysql->options,sizeof(mysql->options)); DBUG_VOID_RETURN; @@ -3205,12 +3688,12 @@ static void mysql_close_free_options(MYSQL *mysql) static void mysql_close_free(MYSQL *mysql) { - my_free((uchar*) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->host_info); + my_free(mysql->user); + my_free(mysql->passwd); + my_free(mysql->db); #if defined(EMBEDDED_LIBRARY) || MYSQL_VERSION_ID >= 50100 - my_free(mysql->info_buffer,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->info_buffer); mysql->info_buffer= 0; #endif /* Clear pointers for better safety */ @@ -3303,6 +3786,30 @@ void mysql_detach_stmt_list(LIST **stmt_list __attribute__((unused)), (As some clients call this after mysql_real_connect() fails) */ +/* + mysql_close() can actually block, at least in theory, if the socket buffer + is full when sending the COM_QUIT command. + + On the other hand, the latter part of mysql_close() needs to free the stack + used for non-blocking operation of blocking stuff, so that later part can + _not_ be done non-blocking. + + Therefore, mysql_close_slow_part() is used to run the parts of mysql_close() + that may block. It can be called before mysql_close(), and in that case + mysql_close() is guaranteed not to need to block. */ +void STDCALL mysql_close_slow_part(MYSQL *mysql) +{ + /* If connection is still up, send a QUIT message */ + if (mysql->net.vio != 0) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + mysql->reconnect=0; + simple_command(mysql,COM_QUIT,(uchar*) 0,0,1); + end_server(mysql); /* Sets mysql->net.vio= 0 */ + } +} + void STDCALL mysql_close(MYSQL *mysql) { DBUG_ENTER("mysql_close"); @@ -3310,38 +3817,10 @@ void STDCALL mysql_close(MYSQL *mysql) if (mysql) /* Some simple safety */ { - /* If connection is still up, send a QUIT message */ - if (mysql->net.vio != 0) - { - free_old_query(mysql); - mysql->status=MYSQL_STATUS_READY; /* Force command */ - mysql->reconnect=0; - simple_command(mysql,COM_QUIT,(uchar*) 0,0,1); - end_server(mysql); /* Sets mysql->net.vio= 0 */ - } + mysql_close_slow_part(mysql); mysql_close_free_options(mysql); mysql_close_free(mysql); mysql_detach_stmt_list(&mysql->stmts, "mysql_close"); -#ifndef TO_BE_DELETED - /* free/close slave list */ - if (mysql->rpl_pivot) - { - MYSQL* tmp; - for (tmp = mysql->next_slave; tmp != mysql; ) - { - /* trick to avoid following freed pointer */ - MYSQL* tmp1 = tmp->next_slave; - mysql_close(tmp); - tmp = tmp1; - } - mysql->rpl_pivot=0; - } -#endif - if (mysql != mysql->master) - { - mysql_close(mysql->master); - mysql->master= 0; - } #ifndef MYSQL_SERVER if (mysql->thd) { @@ -3350,7 +3829,7 @@ void STDCALL mysql_close(MYSQL *mysql) } #endif if (mysql->free_me) - my_free((uchar*) mysql,MYF(0)); + my_free(mysql); } DBUG_VOID_RETURN; } @@ -3364,12 +3843,6 @@ static my_bool cli_read_query_result(MYSQL *mysql) ulong length; DBUG_ENTER("cli_read_query_result"); - /* - Read from the connection which we actually used, which - could differ from the original connection if we have slaves - */ - mysql = mysql->last_used_con; - if ((length = cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); free_old_query(mysql); /* Free old result */ @@ -3444,23 +3917,6 @@ int STDCALL mysql_send_query(MYSQL* mysql, const char* query, ulong length) { DBUG_ENTER("mysql_send_query"); - DBUG_PRINT("enter",("rpl_parse: %d rpl_pivot: %d", - mysql->options.rpl_parse, mysql->rpl_pivot)); -#ifndef TO_BE_DELETED - if (mysql->options.rpl_parse && mysql->rpl_pivot) - { - switch (mysql_rpl_query_type(query, length)) { - case MYSQL_RPL_MASTER: - DBUG_RETURN(mysql_master_send_query(mysql, query, length)); - case MYSQL_RPL_SLAVE: - DBUG_RETURN(mysql_slave_send_query(mysql, query, length)); - case MYSQL_RPL_ADMIN: - break; /* fall through */ - } - } - mysql->last_used_con = mysql; -#endif - DBUG_RETURN(simple_command(mysql, COM_QUERY, (uchar*) query, length, 1)); } @@ -3487,8 +3943,7 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql) { MYSQL_RES *result; DBUG_ENTER("mysql_store_result"); - /* read from the actually used connection */ - mysql = mysql->last_used_con; + if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -3511,7 +3966,7 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql) if (!(result->data= (*mysql->methods->read_rows)(mysql,mysql->fields,mysql->field_count))) { - my_free((uchar*) result,MYF(0)); + my_free(result); DBUG_RETURN(0); } mysql->affected_rows= result->row_count= result->data->rows; @@ -3543,8 +3998,6 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) MYSQL_RES *result; DBUG_ENTER("cli_use_result"); - mysql = mysql->last_used_con; - if (!mysql->fields) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) @@ -3561,7 +4014,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) if (!(result->row=(MYSQL_ROW) my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) { /* Ptrs: to one row */ - my_free((uchar*) result,MYF(0)); + my_free(result); DBUG_RETURN(0); } result->fields= mysql->fields; @@ -3650,10 +4103,15 @@ mysql_fetch_lengths(MYSQL_RES *res) } +#define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) + int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) { - DBUG_ENTER("mysql_option"); + struct mysql_async_context *ctxt; + size_t stacksize; + + DBUG_ENTER("mysql_options"); DBUG_PRINT("enter",("option: %d",(int) option)); switch (option) { case MYSQL_OPT_CONNECT_TIMEOUT: @@ -3682,19 +4140,19 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) add_init_command(&mysql->options,arg); break; case MYSQL_READ_DEFAULT_FILE: - my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_file); mysql->options.my_cnf_file=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_READ_DEFAULT_GROUP: - my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_group); mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_SET_CHARSET_DIR: - my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_dir); mysql->options.charset_dir=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_SET_CHARSET_NAME: - my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_name); mysql->options.charset_name=my_strdup(arg,MYF(MY_WME)); break; case MYSQL_OPT_PROTOCOL: @@ -3703,7 +4161,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) case MYSQL_SHARED_MEMORY_BASE_NAME: #ifdef HAVE_SMEM if (mysql->options.shared_memory_base_name != def_shared_memory_base_name) - my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.shared_memory_base_name); mysql->options.shared_memory_base_name=my_strdup(arg,MYF(MY_WME)); #endif break; @@ -3731,10 +4189,10 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT; break; case MYSQL_PLUGIN_DIR: - extension_set_string(&mysql->options, plugin_dir, arg); + EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg); break; case MYSQL_DEFAULT_AUTH: - extension_set_string(&mysql->options, default_auth, arg); + EXTENSION_SET_STRING(&mysql->options, default_auth, arg); break; case MYSQL_PROGRESS_CALLBACK: if (!mysql->options.extension) @@ -3745,6 +4203,39 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) mysql->options.extension->report_progress= (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg; break; + case MYSQL_OPT_NONBLOCK: + if (mysql->options.extension && + (ctxt = mysql->options.extension->async_context) != 0) + { + /* + We must not allow changing the stack size while a non-blocking call is + suspended (as the stack is then in use). + */ + if (ctxt->suspended) + DBUG_RETURN(1); + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + } + if (!(ctxt= (struct mysql_async_context *) + my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) + { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + DBUG_RETURN(1); + } + stacksize= 0; + if (arg) + stacksize= *(const size_t *)arg; + if (!stacksize) + stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE; + if (my_context_init(&ctxt->async_context, stacksize)) + { + my_free(ctxt); + DBUG_RETURN(1); + } + EXTENSION_SET(&(mysql->options), async_context, ctxt) + if (mysql->net.vio) + mysql->net.vio->async_context= ctxt; + break; default: DBUG_RETURN(1); } @@ -3852,17 +4343,18 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) return mysql->net.last_errno; } - /** client authentication plugin that does native MySQL authentication using a 20-byte (4.1+) scramble */ - static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { int pkt_len; uchar *pkt; + DBUG_ENTER("native_password_auth_client"); + + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* @@ -3876,10 +4368,10 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { /* read the scramble */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); if (pkt_len != SCRAMBLE_LENGTH + 1) - return CR_SERVER_HANDSHAKE_ERR; + DBUG_RETURN(CR_SERVER_HANDSHAKE_ERR); /* save it in MYSQL */ memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH); @@ -3889,28 +4381,32 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) if (mysql->passwd[0]) { char scrambled[SCRAMBLE_LENGTH + 1]; + DBUG_PRINT("info", ("sending scramble")); scramble(scrambled, (char*)pkt, mysql->passwd); if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH)) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); } else + { + DBUG_PRINT("info", ("no password")); if (vio->write_packet(vio, 0, 0)) /* no password */ - return CR_ERROR; + DBUG_RETURN(CR_ERROR); + } - return CR_OK; + DBUG_RETURN(CR_OK); } - /** client authentication plugin that does old MySQL authentication using an 8-byte (4.0-) scramble */ - static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { uchar *pkt; int pkt_len; + DBUG_ENTER("old_password_auth_client"); + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* @@ -3924,14 +4420,14 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { /* read the scramble */ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); if (pkt_len != SCRAMBLE_LENGTH_323 + 1 && pkt_len != SCRAMBLE_LENGTH + 1) - return CR_SERVER_HANDSHAKE_ERR; + DBUG_RETURN(CR_SERVER_HANDSHAKE_ERR); /* save it in MYSQL */ - memcpy(mysql->scramble, pkt, pkt_len); + memmove(mysql->scramble, pkt, pkt_len); mysql->scramble[pkt_len] = 0; } @@ -3940,12 +4436,20 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) char scrambled[SCRAMBLE_LENGTH_323 + 1]; scramble_323(scrambled, (char*)pkt, mysql->passwd); if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1)) - return CR_ERROR; + DBUG_RETURN(CR_ERROR); } else if (vio->write_packet(vio, 0, 0)) /* no password */ - return CR_ERROR; + DBUG_RETURN(CR_ERROR); - return CR_OK; + DBUG_RETURN(CR_OK); } + +my_socket STDCALL +mysql_get_socket(const MYSQL *mysql) +{ + if (mysql->net.vio) + return mysql->net.vio->sd; + return INVALID_SOCKET; +} diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c index 9a90e3c93fd..f31ddb22a6a 100644 --- a/sql-common/client_plugin.c +++ b/sql-common/client_plugin.c @@ -1,4 +1,5 @@ /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab + Copyright (c) 2010, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -11,7 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file @@ -36,11 +37,7 @@ #include "mysql.h" #include <my_sys.h> #include <m_string.h> -#ifdef THREAD #include <my_pthread.h> -#else -#include <my_no_pthread.h> -#endif #include <sql_common.h> #include "errmsg.h" @@ -73,22 +70,21 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= loading the same plugin twice in parallel. */ struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS]; -#ifdef THREAD static pthread_mutex_t LOCK_load_client_plugin; -#endif static int is_not_initialized(MYSQL *mysql, const char *name) { + DBUG_ENTER("is_not_initialized"); + if (initialized) - return 0; + DBUG_RETURN(0); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "not initialized"); - return 1; + DBUG_RETURN(1); } - /** finds a plugin in the list @@ -99,25 +95,25 @@ static int is_not_initialized(MYSQL *mysql, const char *name) @retval a pointer to a found plugin or 0 */ - -static struct st_mysql_client_plugin *find_plugin(const char *name, int type) +static struct st_mysql_client_plugin * +find_plugin(const char *name, int type) { struct st_client_plugin_int *p; + DBUG_ENTER("find_plugin"); DBUG_ASSERT(initialized); DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS); if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) - return 0; + DBUG_RETURN(0); for (p= plugin_list[type]; p; p= p->next) { if (strcmp(p->plugin->name, name) == 0) - return p->plugin; + DBUG_RETURN(p->plugin); } - return NULL; + DBUG_RETURN(NULL); } - /** verifies the plugin and adds it to the list @@ -130,7 +126,6 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) @retval a pointer to an installed plugin or 0 */ - static struct st_mysql_client_plugin * add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, int argc, va_list args) @@ -138,6 +133,7 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, const char *errmsg; struct st_client_plugin_int plugin_int, *p; char errbuf[1024]; + DBUG_ENTER("add_plugin"); DBUG_ASSERT(initialized); @@ -178,22 +174,22 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, p->next= plugin_list[plugin->type]; plugin_list[plugin->type]= p; + net_clear_error(&mysql->net); - return plugin; + DBUG_RETURN(plugin); err2: if (plugin->deinit) plugin->deinit(); err1: - if (dlhandle) - (void)dlclose(dlhandle); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg); - return NULL; + if (dlhandle) + (void)dlclose(dlhandle); + DBUG_RETURN(NULL); } - /** Loads plugins which are specified in the environment variable LIBMYSQL_PLUGINS. @@ -209,14 +205,14 @@ err1: or LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..." */ - static void load_env_plugins(MYSQL *mysql) { char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS"); + DBUG_ENTER("load_env_plugins"); /* no plugins to load */ if (!s) - return; + DBUG_VOID_RETURN; free_env= plugs= my_strdup(s, MYF(MY_WME)); @@ -227,7 +223,8 @@ static void load_env_plugins(MYSQL *mysql) plugs= s + 1; } while (s); - my_free(free_env, MYF(0)); + my_free(free_env); + DBUG_VOID_RETURN; } /********** extern functions to be used by libmysql *********************/ @@ -240,16 +237,16 @@ static void load_env_plugins(MYSQL *mysql) @retval 0 successful @retval != 0 error occured */ - int mysql_client_plugin_init() { MYSQL mysql; struct st_mysql_client_plugin **builtin; va_list unused; + DBUG_ENTER("mysql_client_plugin_init"); LINT_INIT_STRUCT(unused); if (initialized) - return 0; + DBUG_RETURN(0); bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */ @@ -269,23 +266,22 @@ int mysql_client_plugin_init() load_env_plugins(&mysql); - return 0; + DBUG_RETURN(0); } - /** Deinitializes the client plugin layer. Unloades all client plugins and frees any associated resources. */ - void mysql_client_plugin_deinit() { int i; struct st_client_plugin_int *p; + DBUG_ENTER("mysql_client_plugin_deinit"); if (!initialized) - return; + DBUG_VOID_RETURN; for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++) for (p= plugin_list[i]; p; p= p->next) @@ -300,6 +296,7 @@ void mysql_client_plugin_deinit() initialized= 0; free_root(&mem_root, MYF(0)); pthread_mutex_destroy(&LOCK_load_client_plugin); + DBUG_VOID_RETURN; } /************* public facing functions, for client consumption *********/ @@ -310,10 +307,11 @@ mysql_client_register_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin) { va_list unused; + DBUG_ENTER("mysql_client_register_plugin"); LINT_INIT_STRUCT(unused); if (is_not_initialized(mysql, plugin->name)) - return NULL; + DBUG_RETURN(NULL); pthread_mutex_lock(&LOCK_load_client_plugin); @@ -329,10 +327,9 @@ mysql_client_register_plugin(MYSQL *mysql, plugin= add_plugin(mysql, plugin, 0, 0, unused); pthread_mutex_unlock(&LOCK_load_client_plugin); - return plugin; + DBUG_RETURN(plugin); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, @@ -342,9 +339,14 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, char dlpath[FN_REFLEN+1]; void *sym, *dlhandle; struct st_mysql_client_plugin *plugin; + DBUG_ENTER("mysql_load_plugin_v"); + DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc)); if (is_not_initialized(mysql, name)) - return NULL; + { + DBUG_PRINT ("leave", ("mysql not initialized")); + DBUG_RETURN (NULL); + } pthread_mutex_lock(&LOCK_load_client_plugin); @@ -361,9 +363,11 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, mysql->options.extension->plugin_dir : PLUGINDIR, "/", name, SO_EXT, NullS); + DBUG_PRINT ("info", ("dlopeninig %s", dlpath)); /* Open new dll handle */ if (!(dlhandle= dlopen(dlpath, RTLD_NOW))) { + DBUG_PRINT ("info", ("failed to dlopen")); errmsg= dlerror(); goto err; } @@ -399,37 +403,41 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, pthread_mutex_unlock(&LOCK_load_client_plugin); - return plugin; + DBUG_PRINT ("leave", ("plugin loaded ok")); + DBUG_RETURN (plugin); err: pthread_mutex_unlock(&LOCK_load_client_plugin); + DBUG_PRINT ("leave", ("plugin load error : %s", errmsg)); set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg); - return NULL; + DBUG_RETURN (NULL); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...) { struct st_mysql_client_plugin *p; va_list args; + DBUG_ENTER("mysql_load_plugin"); + va_start(args, argc); p= mysql_load_plugin_v(mysql, name, type, argc, args); va_end(args); - return p; + DBUG_RETURN(p); } - /* see <mysql/client_plugin.h> for a full description */ struct st_mysql_client_plugin * mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) { struct st_mysql_client_plugin *p; + DBUG_ENTER("mysql_client_find_plugin"); + DBUG_PRINT ("entry", ("name=%s, type=%d", name, type)); if (is_not_initialized(mysql, name)) - return NULL; + DBUG_RETURN (NULL); if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) { @@ -439,9 +447,26 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) } if ((p= find_plugin(name, type))) - return p; + { + DBUG_PRINT ("leave", ("found %p", p)); + DBUG_RETURN (p); + } /* not found, load it */ - return mysql_load_plugin(mysql, name, type, 0); + p= mysql_load_plugin(mysql, name, type, 0); + DBUG_PRINT ("leave", ("loaded %p", p)); + DBUG_RETURN (p); } + +/* see <mysql/client_plugin.h> for a full description */ +int mysql_plugin_options(struct st_mysql_client_plugin *plugin, + const char *option, + const void *value) +{ + DBUG_ENTER("mysql_plugin_options"); + /* does the plugin support options call? */ + if (!plugin || !plugin->options) + DBUG_RETURN(1); + DBUG_RETURN(plugin->options(option, value)); +} diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 5ad037ab6c9..df75bb49c89 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -12,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <my_time.h> #include <m_string.h> @@ -79,7 +78,7 @@ uint calc_days_in_year(uint year) */ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, - ulong flags, int *was_cut) + ulonglong flags, int *was_cut) { if (not_zero_date) { @@ -161,9 +160,9 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, enum enum_mysql_timestamp_type str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, - ulong flags, int *was_cut) + ulonglong flags, int *was_cut) { - uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields; + uint UNINIT_VAR(field_length), UNINIT_VAR(year_length), digits, i, number_of_fields; uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS]; uint add_hours= 0, start_loop; ulong not_zero_date, allow_space; @@ -176,8 +175,6 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, DBUG_ENTER("str_to_datetime"); DBUG_PRINT("enter",("str: %.*s",length,str)); - LINT_INIT(field_length); - if (flags & TIME_TIME_ONLY) { enum enum_mysql_timestamp_type ret; @@ -493,7 +490,7 @@ err: enum enum_mysql_timestamp_type str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - ulong fuzzydate, int *warning) + ulonglong fuzzydate, int *warning) { ulong date[5]; ulonglong value; @@ -1219,7 +1216,7 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits) */ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res, - uint flags, int *was_cut) + ulonglong flags, int *was_cut) { long part1,part2; @@ -1245,7 +1242,12 @@ longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res, nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */ goto ok; } - if (nr < 10000101L) + /* + Though officially we support DATE values from 1000-01-01 only, one can + easily insert a value like 1-1-1. So, for consistency reasons such dates + are allowed when TIME_FUZZY_DATE is set. + */ + if (nr < 10000101L && !(flags & TIME_FUZZY_DATE)) goto err; if (nr <= 99991231L) { diff --git a/sql-common/my_user.c b/sql-common/my_user.c index 3f9bd619b3c..8d717ea7131 100644 --- a/sql-common/my_user.c +++ b/sql-common/my_user.c @@ -1,5 +1,4 @@ -/* - Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. +/* Copyright (c) 2006-2008 MySQL AB, 2009 Sun Microsystems, Inc. Use is subject to license terms. This program is free software; you can redistribute it and/or modify @@ -13,8 +12,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <my_user.h> #include <m_string.h> diff --git a/sql-common/mysql_async.c b/sql-common/mysql_async.c new file mode 100644 index 00000000000..c130eab5061 --- /dev/null +++ b/sql-common/mysql_async.c @@ -0,0 +1,1947 @@ +/* + Copyright 2011 Kristian Nielsen and Monty Program Ab + + This file is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + MySQL non-blocking client library functions. +*/ + +#include "my_global.h" +#include "my_sys.h" +#include "mysql.h" +#include "errmsg.h" +#include "sql_common.h" +#include "my_context.h" +#include "violite.h" +#include "mysql_async.h" + + +#ifdef __WIN__ +/* + Windows does not support MSG_DONTWAIT for send()/recv(). So we need to ensure + that the socket is non-blocking at the start of every operation. +*/ +#define WIN_SET_NONBLOCKING(mysql) { \ + my_bool old_mode; \ + if ((mysql)->net.vio) vio_blocking((mysql)->net.vio, FALSE, &old_mode); \ + } +#else +#define WIN_SET_NONBLOCKING(mysql) +#endif + + +void +my_context_install_suspend_resume_hook(struct mysql_async_context *b, + void (*hook)(my_bool, void *), + void *user_data) +{ + b->suspend_resume_hook= hook; + b->suspend_resume_hook_user_data= user_data; +} + + +/* Asynchronous connect(); socket must already be set non-blocking. */ +int +my_connect_async(struct mysql_async_context *b, my_socket fd, + const struct sockaddr *name, uint namelen, uint timeout) +{ + int res; + size_socket s_err_size; + + /* Make the socket non-blocking. */ +#ifdef __WIN__ + ulong arg= 1; + ioctlsocket(fd, FIONBIO, (void *)&arg); +#else + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + + b->events_to_wait_for= 0; + /* + Start to connect asynchronously. + If this will block, we suspend the call and return control to the + application context. The application will then resume us when the socket + polls ready for write, indicating that the connection attempt completed. + */ + res= connect(fd, name, namelen); + if (res != 0) + { +#ifdef __WIN__ + int wsa_err= WSAGetLastError(); + if (wsa_err != WSAEWOULDBLOCK) + return res; + b->events_to_wait_for|= MYSQL_WAIT_EXCEPT; +#else + int err= errno; + if (err != EINPROGRESS && err != EALREADY && err != EAGAIN) + return res; +#endif + b->events_to_wait_for|= MYSQL_WAIT_WRITE; + b->timeout_value= timeout; + if (timeout) + b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); + my_context_yield(&b->async_context); + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); + if (b->events_occured & MYSQL_WAIT_TIMEOUT) + return -1; + + s_err_size= sizeof(res); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &res, &s_err_size) != 0) + return -1; + if (res) + { + errno= res; + return -1; + } + } + return res; +} + +#define IS_BLOCKING_ERROR() \ + IF_WIN(WSAGetLastError() != WSAEWOULDBLOCK, \ + (errno != EAGAIN && errno != EINTR)) + +ssize_t +my_recv_async(struct mysql_async_context *b, int fd, + unsigned char *buf, size_t size, uint timeout) +{ + ssize_t res; + + for (;;) + { + res= recv(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + if (res >= 0 || IS_BLOCKING_ERROR()) + return res; + b->events_to_wait_for= MYSQL_WAIT_READ; + if (timeout) + { + b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; + b->timeout_value= timeout; + } + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); + my_context_yield(&b->async_context); + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); + if (b->events_occured & MYSQL_WAIT_TIMEOUT) + return -1; + } +} + + +ssize_t +my_send_async(struct mysql_async_context *b, int fd, + const unsigned char *buf, size_t size, uint timeout) +{ + ssize_t res; + + for (;;) + { + res= send(fd, buf, size, IF_WIN(0, MSG_DONTWAIT)); + if (res >= 0 || IS_BLOCKING_ERROR()) + return res; + b->events_to_wait_for= MYSQL_WAIT_WRITE; + if (timeout) + { + b->events_to_wait_for|= MYSQL_WAIT_TIMEOUT; + b->timeout_value= timeout; + } + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); + my_context_yield(&b->async_context); + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); + if (b->events_occured & MYSQL_WAIT_TIMEOUT) + return -1; + } +} + + +my_bool +my_poll_read_async(struct mysql_async_context *b, uint timeout) +{ + b->events_to_wait_for= MYSQL_WAIT_READ | MYSQL_WAIT_TIMEOUT; + b->timeout_value= timeout; + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); + my_context_yield(&b->async_context); + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); + return (b->events_occured & MYSQL_WAIT_READ) ? 0 : 1; +} + + +#ifdef HAVE_OPENSSL +static my_bool +my_ssl_async_check_result(int res, struct mysql_async_context *b, SSL *ssl) +{ + int ssl_err; + b->events_to_wait_for= 0; + if (res >= 0) + return 1; + ssl_err= SSL_get_error(ssl, res); + if (ssl_err == SSL_ERROR_WANT_READ) + b->events_to_wait_for|= MYSQL_WAIT_READ; + else if (ssl_err == SSL_ERROR_WANT_WRITE) + b->events_to_wait_for|= MYSQL_WAIT_WRITE; + else + return 1; + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); + my_context_yield(&b->async_context); + if (b->suspend_resume_hook) + (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); + return 0; +} + +int +my_ssl_read_async(struct mysql_async_context *b, SSL *ssl, + void *buf, int size) +{ + int res; + + for (;;) + { + res= SSL_read(ssl, buf, size); + if (my_ssl_async_check_result(res, b, ssl)) + return res; + } +} + +int +my_ssl_write_async(struct mysql_async_context *b, SSL *ssl, + const void *buf, int size) +{ + int res; + + for (;;) + { + res= SSL_write(ssl, buf, size); + if (my_ssl_async_check_result(res, b, ssl)) + return res; + } +} +#endif /* HAVE_OPENSSL */ + +unsigned int STDCALL +mysql_get_timeout_value(const MYSQL *mysql) +{ + return mysql->options.extension->async_context->timeout_value; +} + + +/* + Now create non-blocking definitions for all the calls that may block. + + Each call FOO gives rise to FOO_start() that prepares the MYSQL object for + doing non-blocking calls that can suspend operation mid-way, and then starts + the call itself. And a FOO_start_internal trampoline to assist with running + the real call in a co-routine that can be suspended. And a FOO_cont() that + can continue a suspended operation. +*/ + +#define MK_ASYNC_INTERNAL_BODY(call, invoke_args, mysql_val, ret_type, ok_val)\ + struct call ## _params *parms= (struct call ## _params *)d; \ + ret_type ret; \ + struct mysql_async_context *b= \ + (mysql_val)->options.extension->async_context; \ + \ + ret= call invoke_args; \ + b->ret_result. ok_val = ret; \ + b->events_to_wait_for= 0; + +#define MK_ASYNC_START_BODY(call, mysql_val, parms_assign, err_val, ok_val, extra1) \ + int res; \ + struct mysql_async_context *b; \ + struct call ## _params parms; \ + \ + extra1 \ + b= mysql_val->options.extension->async_context; \ + parms_assign \ + \ + b->active= 1; \ + res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \ + b->active= b->suspended= 0; \ + if (res > 0) \ + { \ + /* Suspended. */ \ + b->suspended= 1; \ + return b->events_to_wait_for; \ + } \ + if (res < 0) \ + { \ + set_mysql_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \ + *ret= err_val; \ + } \ + else \ + *ret= b->ret_result. ok_val; \ + return 0; + +#define MK_ASYNC_CONT_BODY(mysql_val, err_val, ok_val) \ + int res; \ + struct mysql_async_context *b= \ + (mysql_val)->options.extension->async_context; \ + if (!b->suspended) \ + { \ + set_mysql_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \ + *ret= err_val; \ + return 0; \ + } \ + \ + b->active= 1; \ + b->events_occured= ready_status; \ + res= my_context_continue(&b->async_context); \ + b->active= 0; \ + if (res > 0) \ + return b->events_to_wait_for; /* (Still) suspended */ \ + b->suspended= 0; \ + if (res < 0) \ + { \ + set_mysql_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \ + *ret= err_val; \ + } \ + else \ + *ret= b->ret_result. ok_val; /* Finished. */ \ + return 0; + +#define MK_ASYNC_INTERNAL_BODY_VOID_RETURN(call, invoke_args, mysql_val) \ + struct call ## _params *parms= (struct call ## _params *)d; \ + struct mysql_async_context *b= \ + (mysql_val)->options.extension->async_context; \ + \ + call invoke_args; \ + b->events_to_wait_for= 0; + +#define MK_ASYNC_START_BODY_VOID_RETURN(call, mysql_val, parms_assign, extra1)\ + int res; \ + struct mysql_async_context *b; \ + struct call ## _params parms; \ + \ + extra1 \ + b= mysql_val->options.extension->async_context; \ + parms_assign \ + \ + b->active= 1; \ + res= my_context_spawn(&b->async_context, call ## _start_internal, &parms); \ + b->active= b->suspended= 0; \ + if (res > 0) \ + { \ + /* Suspended. */ \ + b->suspended= 1; \ + return b->events_to_wait_for; \ + } \ + if (res < 0) \ + set_mysql_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \ + return 0; + +#define MK_ASYNC_CONT_BODY_VOID_RETURN(mysql_val) \ + int res; \ + struct mysql_async_context *b= \ + (mysql_val)->options.extension->async_context; \ + if (!b->suspended) \ + { \ + set_mysql_error((mysql_val), CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); \ + return 0; \ + } \ + \ + b->active= 1; \ + b->events_occured= ready_status; \ + res= my_context_continue(&b->async_context); \ + b->active= 0; \ + if (res > 0) \ + return b->events_to_wait_for; /* (Still) suspended */ \ + b->suspended= 0; \ + if (res < 0) \ + set_mysql_error((mysql_val), CR_OUT_OF_MEMORY, unknown_sqlstate); \ + return 0; + + +/* Structure used to pass parameters from mysql_real_connect_start(). */ +struct mysql_real_connect_params { + MYSQL *mysql; + const char *host; + const char *user; + const char *passwd; + const char *db; + unsigned int port; + const char *unix_socket; + unsigned long client_flags; +}; +static void +mysql_real_connect_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_real_connect, + (parms->mysql, parms->host, parms->user, parms->passwd, parms->db, + parms->port, parms->unix_socket, parms->client_flags), + parms->mysql, + MYSQL *, + r_ptr) +} +int STDCALL +mysql_real_connect_start(MYSQL **ret, MYSQL *mysql, const char *host, + const char *user, const char *passwd, const char *db, + unsigned int port, const char *unix_socket, + unsigned long client_flags) +{ +MK_ASYNC_START_BODY( + mysql_real_connect, + mysql, + { + parms.mysql= mysql; + parms.host= host; + parms.user= user; + parms.passwd= passwd; + parms.db= db; + parms.port= port; + parms.unix_socket= unix_socket; + parms.client_flags= client_flags; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_real_connect_cont(MYSQL **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_real_query_start(). */ +struct mysql_real_query_params { + MYSQL *mysql; + const char *stmt_str; + unsigned long length; +}; +static void +mysql_real_query_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_real_query, + (parms->mysql, parms->stmt_str, parms->length), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_real_query_start(int *ret, MYSQL *mysql, const char *stmt_str, unsigned long length) +{ +MK_ASYNC_START_BODY( + mysql_real_query, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.stmt_str= stmt_str; + parms.length= length; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_real_query_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_fetch_row_start(). */ +struct mysql_fetch_row_params { + MYSQL_RES *result; +}; +static void +mysql_fetch_row_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_fetch_row, + (parms->result), + parms->result->handle, + MYSQL_ROW, + r_ptr) +} +int STDCALL +mysql_fetch_row_start(MYSQL_ROW *ret, MYSQL_RES *result) +{ +MK_ASYNC_START_BODY( + mysql_fetch_row, + result->handle, + { + WIN_SET_NONBLOCKING(result->handle) + parms.result= result; + }, + NULL, + r_ptr, + /* + If we already fetched all rows from server (eg. mysql_store_result()), + then result->handle will be NULL and we cannot suspend. But that is fine, + since in this case mysql_fetch_row cannot block anyway. Just return + directly. + */ + if (!result->handle) + { + *ret= mysql_fetch_row(result); + return 0; + }) +} +int STDCALL +mysql_fetch_row_cont(MYSQL_ROW *ret, MYSQL_RES *result, int ready_status) +{ +MK_ASYNC_CONT_BODY( + result->handle, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_set_character_set_start(). */ +struct mysql_set_character_set_params { + MYSQL *mysql; + const char *csname; +}; +static void +mysql_set_character_set_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_set_character_set, + (parms->mysql, parms->csname), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_set_character_set_start(int *ret, MYSQL *mysql, const char *csname) +{ +MK_ASYNC_START_BODY( + mysql_set_character_set, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.csname= csname; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_set_character_set_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_sekect_db_start(). */ +struct mysql_select_db_params { + MYSQL *mysql; + const char *db; +}; +static void +mysql_select_db_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_select_db, + (parms->mysql, parms->db), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_select_db_start(int *ret, MYSQL *mysql, const char *db) +{ +MK_ASYNC_START_BODY( + mysql_select_db, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.db= db; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_select_db_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_send_query_start(). */ +struct mysql_send_query_params { + MYSQL *mysql; + const char *q; + unsigned long length; +}; +static void +mysql_send_query_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_send_query, + (parms->mysql, parms->q, parms->length), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_send_query_start(int *ret, MYSQL *mysql, const char *q, unsigned long length) +{ +MK_ASYNC_START_BODY( + mysql_send_query, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.q= q; + parms.length= length; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_send_query_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_store_result_start(). */ +struct mysql_store_result_params { + MYSQL *mysql; +}; +static void +mysql_store_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_store_result, + (parms->mysql), + parms->mysql, + MYSQL_RES *, + r_ptr) +} +int STDCALL +mysql_store_result_start(MYSQL_RES **ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_store_result, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_store_result_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_free_result_start(). */ +struct mysql_free_result_params { + MYSQL_RES *result; +}; +static void +mysql_free_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY_VOID_RETURN( + mysql_free_result, + (parms->result), + parms->result->handle) +} +int STDCALL +mysql_free_result_start(MYSQL_RES *result) +{ +MK_ASYNC_START_BODY_VOID_RETURN( + mysql_free_result, + result->handle, + { + WIN_SET_NONBLOCKING(result->handle) + parms.result= result; + }, + /* + mysql_free_result() can have NULL in result->handle (this happens when all + rows have been fetched and mysql_fetch_row() returned NULL.) + So we cannot suspend, but it does not matter, as in this case + mysql_free_result() cannot block. + It is also legitimate to have NULL result, which will do nothing. + */ + if (!result || !result->handle) + { + mysql_free_result(result); + return 0; + }) +} +int STDCALL +mysql_free_result_cont(MYSQL_RES *result, int ready_status) +{ +MK_ASYNC_CONT_BODY_VOID_RETURN(result->handle) +} + +/* Structure used to pass parameters from mysql_close_slow_part_start(). */ +struct mysql_close_slow_part_params { + MYSQL *sock; +}; +/* + We need special handling for mysql_close(), as the first part may block, + while the last part needs to free our extra library context stack. + + So we do the first part (mysql_close_slow_part()) non-blocking, but the last + part blocking. +*/ +static void +mysql_close_slow_part_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY_VOID_RETURN( + mysql_close_slow_part, + (parms->sock), + parms->sock) +} +int STDCALL +mysql_close_slow_part_start(MYSQL *sock) +{ +MK_ASYNC_START_BODY_VOID_RETURN( + mysql_close_slow_part, + sock, + { + WIN_SET_NONBLOCKING(sock) + parms.sock= sock; + }, + /* Nothing */) +} +int STDCALL +mysql_close_slow_part_cont(MYSQL *sock, int ready_status) +{ +MK_ASYNC_CONT_BODY_VOID_RETURN(sock) +} +int STDCALL +mysql_close_start(MYSQL *sock) +{ + int res; + + /* It is legitimate to have NULL sock argument, which will do nothing. */ + if (sock) + { + res= mysql_close_slow_part_start(sock); + /* If we need to block, return now and do the rest in mysql_close_cont(). */ + if (res) + return res; + } + mysql_close(sock); + return 0; +} +int STDCALL +mysql_close_cont(MYSQL *sock, int ready_status) +{ + int res; + + res= mysql_close_slow_part_cont(sock, ready_status); + if (res) + return res; + mysql_close(sock); + return 0; +} + +/* + These following are not available inside the server (neither blocking or + non-blocking). +*/ +#ifndef MYSQL_SERVER +/* Structure used to pass parameters from mysql_change_user_start(). */ +struct mysql_change_user_params { + MYSQL *mysql; + const char *user; + const char *passwd; + const char *db; +}; +static void +mysql_change_user_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_change_user, + (parms->mysql, parms->user, parms->passwd, parms->db), + parms->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_change_user_start(my_bool *ret, MYSQL *mysql, const char *user, const char *passwd, const char *db) +{ +MK_ASYNC_START_BODY( + mysql_change_user, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.user= user; + parms.passwd= passwd; + parms.db= db; + }, + TRUE, + r_my_bool, + /* Nothing */) +} +int STDCALL +mysql_change_user_cont(my_bool *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_query_start(). */ +struct mysql_query_params { + MYSQL *mysql; + const char *q; +}; +static void +mysql_query_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_query, + (parms->mysql, parms->q), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_query_start(int *ret, MYSQL *mysql, const char *q) +{ +MK_ASYNC_START_BODY( + mysql_query, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.q= q; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_query_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_shutdown_start(). */ +struct mysql_shutdown_params { + MYSQL *mysql; + enum mysql_enum_shutdown_level shutdown_level; +}; +static void +mysql_shutdown_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_shutdown, + (parms->mysql, parms->shutdown_level), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_shutdown_start(int *ret, MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) +{ +MK_ASYNC_START_BODY( + mysql_shutdown, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.shutdown_level= shutdown_level; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_shutdown_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_dump_debug_info_start(). */ +struct mysql_dump_debug_info_params { + MYSQL *mysql; +}; +static void +mysql_dump_debug_info_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_dump_debug_info, + (parms->mysql), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_dump_debug_info_start(int *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_dump_debug_info, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_dump_debug_info_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_refresh_start(). */ +struct mysql_refresh_params { + MYSQL *mysql; + unsigned int refresh_options; +}; +static void +mysql_refresh_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_refresh, + (parms->mysql, parms->refresh_options), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_refresh_start(int *ret, MYSQL *mysql, unsigned int refresh_options) +{ +MK_ASYNC_START_BODY( + mysql_refresh, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.refresh_options= refresh_options; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_refresh_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_kill_start(). */ +struct mysql_kill_params { + MYSQL *mysql; + unsigned long pid; +}; +static void +mysql_kill_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_kill, + (parms->mysql, parms->pid), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_kill_start(int *ret, MYSQL *mysql, unsigned long pid) +{ +MK_ASYNC_START_BODY( + mysql_kill, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.pid= pid; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_kill_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_set_server_option_start(). */ +struct mysql_set_server_option_params { + MYSQL *mysql; + enum enum_mysql_set_option option; +}; +static void +mysql_set_server_option_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_set_server_option, + (parms->mysql, parms->option), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_set_server_option_start(int *ret, MYSQL *mysql, + enum enum_mysql_set_option option) +{ +MK_ASYNC_START_BODY( + mysql_set_server_option, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.option= option; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_set_server_option_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_ping_start(). */ +struct mysql_ping_params { + MYSQL *mysql; +}; +static void +mysql_ping_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_ping, + (parms->mysql), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_ping_start(int *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_ping, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_ping_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stat_start(). */ +struct mysql_stat_params { + MYSQL *mysql; +}; +static void +mysql_stat_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stat, + (parms->mysql), + parms->mysql, + const char *, + r_const_ptr) +} +int STDCALL +mysql_stat_start(const char **ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_stat, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + NULL, + r_const_ptr, + /* Nothing */) +} +int STDCALL +mysql_stat_cont(const char **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_const_ptr) +} + +/* Structure used to pass parameters from mysql_list_dbs_start(). */ +struct mysql_list_dbs_params { + MYSQL *mysql; + const char *wild; +}; +static void +mysql_list_dbs_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_list_dbs, + (parms->mysql, parms->wild), + parms->mysql, + MYSQL_RES *, + r_ptr) +} +int STDCALL +mysql_list_dbs_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild) +{ +MK_ASYNC_START_BODY( + mysql_list_dbs, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.wild= wild; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_list_dbs_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_list_tables_start(). */ +struct mysql_list_tables_params { + MYSQL *mysql; + const char *wild; +}; +static void +mysql_list_tables_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_list_tables, + (parms->mysql, parms->wild), + parms->mysql, + MYSQL_RES *, + r_ptr) +} +int STDCALL +mysql_list_tables_start(MYSQL_RES **ret, MYSQL *mysql, const char *wild) +{ +MK_ASYNC_START_BODY( + mysql_list_tables, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.wild= wild; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_list_tables_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_list_processes_start(). */ +struct mysql_list_processes_params { + MYSQL *mysql; +}; +static void +mysql_list_processes_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_list_processes, + (parms->mysql), + parms->mysql, + MYSQL_RES *, + r_ptr) +} +int STDCALL +mysql_list_processes_start(MYSQL_RES **ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_list_processes, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_list_processes_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_list_fields_start(). */ +struct mysql_list_fields_params { + MYSQL *mysql; + const char *table; + const char *wild; +}; +static void +mysql_list_fields_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_list_fields, + (parms->mysql, parms->table, parms->wild), + parms->mysql, + MYSQL_RES *, + r_ptr) +} +int STDCALL +mysql_list_fields_start(MYSQL_RES **ret, MYSQL *mysql, const char *table, + const char *wild) +{ +MK_ASYNC_START_BODY( + mysql_list_fields, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.table= table; + parms.wild= wild; + }, + NULL, + r_ptr, + /* Nothing */) +} +int STDCALL +mysql_list_fields_cont(MYSQL_RES **ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + NULL, + r_ptr) +} + +/* Structure used to pass parameters from mysql_read_query_result_start(). */ +struct mysql_read_query_result_params { + MYSQL *mysql; +}; +static void +mysql_read_query_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_read_query_result, + (parms->mysql), + parms->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_read_query_result_start(my_bool *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_read_query_result, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + TRUE, + r_my_bool, + /* Nothing */) +} +int STDCALL +mysql_read_query_result_cont(my_bool *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_stmt_prepare_start(). */ +struct mysql_stmt_prepare_params { + MYSQL_STMT *stmt; + const char *query; + unsigned long length; +}; +static void +mysql_stmt_prepare_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_prepare, + (parms->stmt, parms->query, parms->length), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_prepare_start(int *ret, MYSQL_STMT *stmt, const char *query, + unsigned long length) +{ +MK_ASYNC_START_BODY( + mysql_stmt_prepare, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + parms.query= query; + parms.length= length; + }, + 1, + r_int, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_prepare(stmt, query, length); + return 0; + }) +} +int STDCALL +mysql_stmt_prepare_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stmt_execute_start(). */ +struct mysql_stmt_execute_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_execute_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_execute, + (parms->stmt), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_execute_start(int *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_execute, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + 1, + r_int, + /* + If eg. mysql_change_user(), stmt->mysql will be NULL. + In this case, we cannot block. + */ + if (!stmt->mysql) + { + *ret= mysql_stmt_execute(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_execute_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stmt_fetch_start(). */ +struct mysql_stmt_fetch_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_fetch_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_fetch, + (parms->stmt), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_fetch_start(int *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_fetch, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + 1, + r_int, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_fetch(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_fetch_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stmt_store_result_start(). */ +struct mysql_stmt_store_result_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_store_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_store_result, + (parms->stmt), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_store_result_start(int *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_store_result, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + 1, + r_int, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_store_result(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_store_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stmt_close_start(). */ +struct mysql_stmt_close_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_close_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_close, + (parms->stmt), + parms->stmt->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_stmt_close_start(my_bool *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_close, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + TRUE, + r_my_bool, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_close(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_close_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_stmt_reset_start(). */ +struct mysql_stmt_reset_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_reset_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_reset, + (parms->stmt), + parms->stmt->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_stmt_reset_start(my_bool *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_reset, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + TRUE, + r_my_bool, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_reset(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_reset_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_stmt_free_result_start(). */ +struct mysql_stmt_free_result_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_free_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_free_result, + (parms->stmt), + parms->stmt->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_stmt_free_result_start(my_bool *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_free_result, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + TRUE, + r_my_bool, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_free_result(stmt); + return 0; + }) +} +int STDCALL +mysql_stmt_free_result_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_stmt_send_long_data_start(). */ +struct mysql_stmt_send_long_data_params { + MYSQL_STMT *stmt; + unsigned int param_number; + const char *data; + unsigned long length; +}; +static void +mysql_stmt_send_long_data_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_send_long_data, + (parms->stmt, parms->param_number, parms->data, parms->length), + parms->stmt->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_stmt_send_long_data_start(my_bool *ret, MYSQL_STMT *stmt, + unsigned int param_number, + const char *data, unsigned long length) +{ +MK_ASYNC_START_BODY( + mysql_stmt_send_long_data, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + parms.param_number= param_number; + parms.data= data; + parms.length= length; + }, + TRUE, + r_my_bool, + /* If stmt->mysql==NULL then we will not block so can call directly. */ + if (!stmt->mysql) + { + *ret= mysql_stmt_send_long_data(stmt, param_number, data, length); + return 0; + }) +} +int STDCALL +mysql_stmt_send_long_data_cont(my_bool *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_commit_start(). */ +struct mysql_commit_params { + MYSQL *mysql; +}; +static void +mysql_commit_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_commit, + (parms->mysql), + parms->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_commit_start(my_bool *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_commit, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + TRUE, + r_my_bool, + /* Nothing */) +} +int STDCALL +mysql_commit_cont(my_bool *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_rollback_start(). */ +struct mysql_rollback_params { + MYSQL *mysql; +}; +static void +mysql_rollback_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_rollback, + (parms->mysql), + parms->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_rollback_start(my_bool *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_rollback, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + TRUE, + r_my_bool, + /* Nothing */) +} +int STDCALL +mysql_rollback_cont(my_bool *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_autocommit_start(). */ +struct mysql_autocommit_params { + MYSQL *mysql; + my_bool auto_mode; +}; +static void +mysql_autocommit_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_autocommit, + (parms->mysql, parms->auto_mode), + parms->mysql, + my_bool, + r_my_bool) +} +int STDCALL +mysql_autocommit_start(my_bool *ret, MYSQL *mysql, my_bool auto_mode) +{ +MK_ASYNC_START_BODY( + mysql_autocommit, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + parms.auto_mode= auto_mode; + }, + TRUE, + r_my_bool, + /* Nothing */) +} +int STDCALL +mysql_autocommit_cont(my_bool *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + TRUE, + r_my_bool) +} + +/* Structure used to pass parameters from mysql_next_result_start(). */ +struct mysql_next_result_params { + MYSQL *mysql; +}; +static void +mysql_next_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_next_result, + (parms->mysql), + parms->mysql, + int, + r_int) +} +int STDCALL +mysql_next_result_start(int *ret, MYSQL *mysql) +{ +MK_ASYNC_START_BODY( + mysql_next_result, + mysql, + { + WIN_SET_NONBLOCKING(mysql) + parms.mysql= mysql; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_next_result_cont(int *ret, MYSQL *mysql, int ready_status) +{ +MK_ASYNC_CONT_BODY( + mysql, + 1, + r_int) +} + +/* Structure used to pass parameters from mysql_stmt_next_result_start(). */ +struct mysql_stmt_next_result_params { + MYSQL_STMT *stmt; +}; +static void +mysql_stmt_next_result_start_internal(void *d) +{ +MK_ASYNC_INTERNAL_BODY( + mysql_stmt_next_result, + (parms->stmt), + parms->stmt->mysql, + int, + r_int) +} +int STDCALL +mysql_stmt_next_result_start(int *ret, MYSQL_STMT *stmt) +{ +MK_ASYNC_START_BODY( + mysql_stmt_next_result, + stmt->mysql, + { + WIN_SET_NONBLOCKING(stmt->mysql) + parms.stmt= stmt; + }, + 1, + r_int, + /* Nothing */) +} +int STDCALL +mysql_stmt_next_result_cont(int *ret, MYSQL_STMT *stmt, int ready_status) +{ +MK_ASYNC_CONT_BODY( + stmt->mysql, + 1, + r_int) +} +#endif + + +/* + The following functions are deprecated, and so have no non-blocking version: + + mysql_connect + mysql_create_db + mysql_drop_db +*/ + +/* + The following functions can newer block, and so do not have special + non-blocking versions: + + mysql_num_rows() + mysql_num_fields() + mysql_eof() + mysql_fetch_field_direct() + mysql_fetch_fields() + mysql_row_tell() + mysql_field_tell() + mysql_field_count() + mysql_affected_rows() + mysql_insert_id() + mysql_errno() + mysql_error() + mysql_sqlstate() + mysql_warning_count() + mysql_info() + mysql_thread_id() + mysql_character_set_name() + mysql_init() + mysql_ssl_set() + mysql_get_ssl_cipher() + mysql_use_result() + mysql_get_character_set_info() + mysql_set_local_infile_handler() + mysql_set_local_infile_default() + mysql_get_server_info() + mysql_get_server_name() + mysql_get_client_info() + mysql_get_client_version() + mysql_get_host_info() + mysql_get_server_version() + mysql_get_proto_info() + mysql_options() + mysql_data_seek() + mysql_row_seek() + mysql_field_seek() + mysql_fetch_lengths() + mysql_fetch_field() + mysql_escape_string() + mysql_hex_string() + mysql_real_escape_string() + mysql_debug() + myodbc_remove_escape() + mysql_thread_safe() + mysql_embedded() + mariadb_connection() + mysql_stmt_init() + mysql_stmt_fetch_column() + mysql_stmt_param_count() + mysql_stmt_attr_set() + mysql_stmt_attr_get() + mysql_stmt_bind_param() + mysql_stmt_bind_result() + mysql_stmt_result_metadata() + mysql_stmt_param_metadata() + mysql_stmt_errno() + mysql_stmt_error() + mysql_stmt_sqlstate() + mysql_stmt_row_seek() + mysql_stmt_row_tell() + mysql_stmt_data_seek() + mysql_stmt_num_rows() + mysql_stmt_affected_rows() + mysql_stmt_insert_id() + mysql_stmt_field_count() + mysql_more_results() + mysql_get_socket() + mysql_get_timeout_value() +*/ |