diff options
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/client.c | 472 | ||||
-rw-r--r-- | sql-common/client_plugin.c | 96 | ||||
-rw-r--r-- | sql-common/my_time.c | 317 | ||||
-rw-r--r-- | sql-common/mysql_async.c | 1947 |
4 files changed, 2560 insertions, 272 deletions
diff --git a/sql-common/client.c b/sql-common/client.c index 16beb27978e..9f58281ddbf 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2003, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, Monty Program 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 @@ -111,10 +112,12 @@ 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" - uint mysql_port=0; char *mysql_unix_port= 0; const char *unknown_sqlstate= "HY000"; @@ -128,6 +131,7 @@ const char *def_shared_memory_base_name= default_shared_memory_base_name; static void mysql_close_free_options(MYSQL *mysql); 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__) static int wait_for_data(my_socket fd, uint timeout); @@ -731,6 +735,7 @@ cli_safe_read(MYSQL *mysql) NET *net= &mysql->net; ulong len=0; +restart: if (net->vio != 0) len=my_net_read(net); @@ -751,13 +756,27 @@ cli_safe_read(MYSQL *mysql) { if (len > 3) { - char *pos=(char*) net->read_pos+1; - net->last_errno=uint2korr(pos); + uchar *pos= net->read_pos+1; + uint last_errno=uint2korr(pos); + + if (last_errno == 65535 && + (mysql->server_capabilities & CLIENT_PROGRESS)) + { + if (cli_report_progress(mysql, pos+2, (uint) (len-3))) + { + /* Wrong packet */ + set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate); + return (packet_error); + } + goto restart; + } + net->last_errno= last_errno; + pos+=2; len-=2; - if (protocol_41(mysql) && pos[0] == '#') + if (protocol_41(mysql) && (char) pos[0] == '#') { - strmake(net->sqlstate, pos+1, SQLSTATE_LENGTH); + strmake(net->sqlstate, (char*) pos+1, SQLSTATE_LENGTH); pos+= SQLSTATE_LENGTH+1; } else @@ -1010,6 +1029,40 @@ static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results) } +/* + Report progress to the client + + RETURN VALUES + 0 ok + 1 error +*/ + +static int cli_report_progress(MYSQL *mysql, uchar *packet, uint length) +{ + uint stage, max_stage, proc_length; + double progress; + uchar *start= packet; + + if (length < 5) + return 1; /* Wrong packet */ + + if (!(mysql->options.extension && mysql->options.extension->report_progress)) + return 0; /* No callback, ignore packet */ + + packet++; /* Ignore number of strings */ + stage= (uint) *packet++; + max_stage= (uint) *packet++; + progress= uint3korr(packet)/1000.0; + packet+= 3; + proc_length= net_field_length(&packet); + if (packet + proc_length > start + length) + return 1; /* Wrong packet */ + (*mysql->options.extension->report_progress)(mysql, stage, max_stage, + progress, (char*) packet, + proc_length); + return 0; +} + #ifdef __WIN__ static my_bool is_NT(void) { @@ -1018,57 +1071,6 @@ static my_bool is_NT(void) } #endif - -#ifdef CHECK_LICENSE -/** - Check server side variable 'license'. - - If the variable does not exist or does not contain 'Commercial', - we're talking to non-commercial server from commercial client. - - @retval 0 success - @retval !0 network error or the server is not commercial. - Error code is saved in mysql->net.last_errno. -*/ - -static int check_license(MYSQL *mysql) -{ - MYSQL_ROW row; - MYSQL_RES *res; - NET *net= &mysql->net; - static const char query[]= "SELECT @@license"; - static const char required_license[]= STRINGIFY_ARG(LICENSE); - - if (mysql_real_query(mysql, query, sizeof(query)-1)) - { - if (net->last_errno == ER_UNKNOWN_SYSTEM_VARIABLE) - { - set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate, - ER(CR_WRONG_LICENSE), required_license); - } - return 1; - } - if (!(res= mysql_use_result(mysql))) - return 1; - row= mysql_fetch_row(res); - /* - If no rows in result set, or column value is NULL (none of these - two is ever true for server variables now), or column value - mismatch, set wrong license error. - */ - if (!net->last_errno && - (!row || !row[0] || - strncmp(row[0], required_license, sizeof(required_license)))) - { - set_mysql_extended_error(mysql, CR_WRONG_LICENSE, unknown_sqlstate, - ER(CR_WRONG_LICENSE), required_license); - } - mysql_free_result(res); - return net->last_errno; -} -#endif /* CHECK_LICENSE */ - - /************************************************************************** Shut down connection **************************************************************************/ @@ -1167,7 +1169,7 @@ 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*),0,5); + init_dynamic_array(options->init_commands,sizeof(char*),5,5); } if (!(tmp= my_strdup(cmd,MYF(MY_WME))) || @@ -1180,21 +1182,24 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd) return 0; } -#define EXTENSION_SET_STRING(OPTS, X, STR) \ - if ((OPTS)->extension) \ - my_free((OPTS)->extension->X); \ - else \ +#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= my_strdup((STR), MYF(MY_WME)); + (OPTS)->extension->X= VAL; + +#define EXTENSION_SET_STRING(OPTS, X, STR) \ + if ((OPTS)->extension) \ + 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) { int argc; char *argv_buff[1],**argv; - const char *groups[3]; + const char *groups[5]; DBUG_ENTER("mysql_read_default_options"); DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); @@ -1202,7 +1207,11 @@ void mysql_read_default_options(struct st_mysql_options *options, array_elements(default_options)); argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; - groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0; + groups[0]= (char*) "client"; + groups[1]= (char*) "client-server"; + groups[2]= (char*) "client-mariadb"; + groups[3]= (char*) group; + groups[4]=0; my_load_defaults(filename, groups, &argc, &argv, NULL); if (argc != 1) /* If some default option */ @@ -1250,7 +1259,7 @@ void mysql_read_default_options(struct st_mysql_options *options, break; case OPT_pipe: options->protocol = MYSQL_PROTOCOL_PIPE; - case OPT_connect_timeout: + case OPT_connect_timeout: case OPT_timeout: if (opt_arg) options->connect_timeout=atoi(opt_arg); @@ -1272,7 +1281,7 @@ void mysql_read_default_options(struct st_mysql_options *options, options->host=my_strdup(opt_arg,MYF(MY_WME)); } break; - case OPT_database: + case OPT_database: if (opt_arg) { my_free(options->db); @@ -1324,16 +1333,16 @@ void mysql_read_default_options(struct st_mysql_options *options, my_free(options->charset_name); options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); break; - case OPT_interactive_timeout: + case OPT_interactive_timeout: options->client_flag|= CLIENT_INTERACTIVE; break; - case OPT_local_infile: + case OPT_local_infile: if (!opt_arg || atoi(opt_arg) != 0) options->client_flag|= CLIENT_LOCAL_FILES; else options->client_flag&= ~CLIENT_LOCAL_FILES; break; - case OPT_disable_local_infile: + case OPT_disable_local_infile: options->client_flag&= ~CLIENT_LOCAL_FILES; break; case OPT_max_allowed_packet: @@ -1355,11 +1364,11 @@ void mysql_read_default_options(struct st_mysql_options *options, options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME)); #endif break; - case OPT_multi_results: + case OPT_multi_results: options->client_flag|= CLIENT_MULTI_RESULTS; break; - case OPT_multi_statements: - case OPT_multi_queries: + case OPT_multi_statements: + case OPT_multi_queries: options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS; break; case OPT_secure_auth: @@ -1755,6 +1764,7 @@ mysql_init(MYSQL *mysql) */ mysql->reconnect= 0; + DBUG_PRINT("mysql",("mysql: 0x%lx", (long) mysql)); return mysql; } @@ -1776,6 +1786,11 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , { DBUG_ENTER("mysql_ssl_set"); #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); @@ -1858,7 +1873,7 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) */ -#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) +#if defined(HAVE_OPENSSL) static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const char **errptr) { @@ -1916,7 +1931,7 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const c DBUG_RETURN(1); } -#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ +#endif /* HAVE_OPENSSL */ /* @@ -2193,6 +2208,7 @@ mysql_autodetect_character_set(MYSQL *mysql) } #endif + my_free(mysql->options.charset_name); if (!(mysql->options.charset_name= my_strdup(csname, MYF(MY_WME)))) return 1; return 0; @@ -2275,7 +2291,6 @@ typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int); static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); -static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); static auth_plugin_t native_password_client_plugin= { @@ -2309,39 +2324,13 @@ static auth_plugin_t old_password_client_plugin= old_password_auth_client }; -static auth_plugin_t clear_password_client_plugin= -{ - MYSQL_CLIENT_AUTHENTICATION_PLUGIN, - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - "mysql_clear_password", - "Georgi Kodinov", - "Clear password authentication plugin", - {0,1,0}, - "GPL", - NULL, - NULL, - NULL, - NULL, - clear_password_auth_client -}; - -#ifdef AUTHENTICATION_WIN -extern auth_plugin_t win_auth_client_plugin; -#endif - struct st_mysql_client_plugin *mysql_client_builtins[]= { (struct st_mysql_client_plugin *)&native_password_client_plugin, (struct st_mysql_client_plugin *)&old_password_client_plugin, - (struct st_mysql_client_plugin *)&clear_password_client_plugin, -#ifdef AUTHENTICATION_WIN - (struct st_mysql_client_plugin *)&win_auth_client_plugin, -#endif 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); @@ -2386,7 +2375,7 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio, char *buff, *end; int res= 1; - buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN); + buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1); end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1; @@ -2431,6 +2420,7 @@ error: return res; } + /** sends a client authentication packet (second packet in the 3-way handshake) @@ -2561,7 +2551,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, ER(CR_SSL_CONNECTION_ERROR), buf); goto error; - } + } DBUG_PRINT("info", ("IO layer change done!")); /* Verify server cert */ @@ -2635,6 +2625,7 @@ error: return 1; } + /** vio->read_packet() callback method for client authentication plugins @@ -2693,6 +2684,7 @@ static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) return pkt_len; } + /** vio->write_packet() callback method for client authentication plugins @@ -2732,6 +2724,7 @@ static int client_mpvio_write_packet(struct st_plugin_vio *mpv, return res; } + /** fills MYSQL_PLUGIN_VIO_INFO structure with the information about the connection @@ -2751,7 +2744,7 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) case VIO_TYPE_SSL: { struct sockaddr addr; - socklen_t addrlen= sizeof(addr); + SOCKET_SIZE_TYPE addrlen= sizeof(addr); if (getsockname(vio->sd, &addr, &addrlen)) return; info->protocol= addr.sa_family == AF_UNIX ? @@ -2764,17 +2757,18 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info) info->protocol= MYSQL_VIO_PIPE; info->handle= vio->hPipe; return; -#ifdef HAVE_SMEM case VIO_TYPE_SHARED_MEMORY: info->protocol= MYSQL_VIO_MEMORY; +#ifdef HAVE_SMEM info->handle= vio->handle_file_map; /* or what ? */ - return; #endif + return; #endif default: DBUG_ASSERT(0); } } + static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, MYSQL_PLUGIN_VIO_INFO *info) { @@ -2782,6 +2776,7 @@ static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, mpvio_info(mpvio->mysql->net.vio, info); } + /** Client side of the plugin driver authentication. @@ -2892,7 +2887,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, { /* The server asked to use a different authentication plugin */ 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; @@ -2900,7 +2895,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; } else - { + { /* new "use different plugin" packet */ uint len; auth_plugin_name= (char*)mysql->net.read_pos + 1; @@ -2954,6 +2949,23 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, 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, @@ -2966,10 +2978,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, const char *scramble_plugin; 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 @@ -2977,6 +2985,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, struct sockaddr_un UNIXaddr; #endif DBUG_ENTER("mysql_real_connect"); + LINT_INIT(pkt_scramble_len); DBUG_PRINT("enter",("host: %s db: %s user: %s (client)", host ? host : "(Null)", @@ -3041,7 +3050,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, #if defined(HAVE_SMEM) if ((!mysql->options.protocol || mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) && - (!host || !strcmp(host,LOCAL_HOST))) + (!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)) == @@ -3051,7 +3061,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ("host: '%s' socket: '%s' shared memory: %s have_tcpip: %d", host ? host : "<null>", unix_socket ? unix_socket : "<null>", - (int) mysql->options.shared_memory_base_name, + mysql->options.shared_memory_base_name, (int) have_tcpip)); if (mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) goto error; @@ -3110,9 +3120,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, 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)); @@ -3177,17 +3186,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, 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 - DBUG_PRINT("info",("IP '%s'", "client")); -#ifdef MYSQL_SERVER - thr_end_alarm(&alarmed); -#endif - memset(&hints, 0, sizeof(hints)); hints.ai_socktype= SOCK_STREAM; hints.ai_protocol= IPPROTO_TCP; @@ -3227,9 +3227,18 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, continue; } + net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); + if (!net->vio) + { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + closesocket(sock); + freeaddrinfo(res_lst); + goto error; + } + DBUG_PRINT("info", ("Connect socket")); - status= my_connect(sock, t_res->ai_addr, t_res->ai_addrlen, - mysql->options.connect_timeout); + 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 @@ -3245,7 +3254,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, saved_error= socket_errno; DBUG_PRINT("info", ("No success, close socket, try next address.")); - closesocket(sock); + vio_delete(mysql->net.vio); + mysql->net.vio= 0; } DBUG_PRINT("info", ("End of connect attempts, sock: %d status: %d error: %d", @@ -3267,15 +3277,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ER(CR_CONN_HOST_ERROR), host, saved_error); goto error; } - - net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); - 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; - } } DBUG_PRINT("info", ("net->vio: %p", net->vio)); @@ -3286,6 +3287,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); @@ -3452,11 +3456,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, if (mysql->client_flag & CLIENT_COMPRESS) /* We will use compression */ net->compress=1; -#ifdef CHECK_LICENSE - if (check_license(mysql)) - goto error; -#endif - if (db && !mysql->db && mysql_select_db(mysql, db)) { if (mysql->net.last_errno == CR_SERVER_LOST) @@ -3516,16 +3515,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); } +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) +{ + struct my_hook_data *hook_data= (struct my_hook_data *)data; + if (suspend) + { + hook_data->orig_vio= hook_data->orig_mysql->net.vio; + hook_data->orig_mysql->net.vio= hook_data->new_mysql->net.vio; + } + else + hook_data->orig_mysql->net.vio= hook_data->orig_vio; +} + 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)); @@ -3542,10 +3564,31 @@ my_bool mysql_reconnect(MYSQL *mysql) tmp_mysql.options= mysql->options; tmp_mysql.options.my_cnf_file= tmp_mysql.options.my_cnf_group= 0; + /* + 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); @@ -3556,11 +3599,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; @@ -3639,8 +3686,14 @@ static void mysql_close_free_options(MYSQL *mysql) #endif /* HAVE_SMEM */ if (mysql->options.extension) { + 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)); @@ -3741,26 +3794,54 @@ void mysql_detach_stmt_list(LIST **stmt_list __attribute__((unused)), } +/* + Close a MySQL connection and free all resources attached to it. + + This function is coded in such that it can be called multiple times + (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"); + DBUG_PRINT("enter", ("mysql: 0x%lx", (long) 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 MYSQL_SERVER if (mysql->thd) + { (*mysql->methods->free_embedded_thd)(mysql); + mysql->thd= 0; + } #endif if (mysql->free_me) my_free(mysql); @@ -4037,10 +4118,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: @@ -4123,6 +4209,48 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) case MYSQL_DEFAULT_AUTH: EXTENSION_SET_STRING(&mysql->options, default_auth, arg); break; + case MYSQL_PROGRESS_CALLBACK: + if (!mysql->options.extension) + mysql->options.extension= (struct st_mysql_options_extention *) + my_malloc(sizeof(struct st_mysql_options_extention), + MYF(MY_WME | MY_ZEROFILL)); + if (mysql->options.extension) + 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); } @@ -4166,7 +4294,7 @@ const char * STDCALL mysql_error(MYSQL *mysql) mysql Connection EXAMPLE - 4.1.0-alfa -> 40100 + MariaDB-4.1.0-alfa -> 40100 NOTES We will ensure that a newer server always has a bigger number. @@ -4179,7 +4307,11 @@ ulong STDCALL mysql_get_server_version(MYSQL *mysql) { uint major, minor, version; - char *pos= mysql->server_version, *end_pos; + const char *pos= mysql->server_version; + char *end_pos; + /* Skip possible prefix */ + while (*pos && !my_isdigit(&my_charset_latin1, *pos)) + pos++; major= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1; minor= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1; version= (uint) strtoul(pos, &end_pos, 10); @@ -4195,7 +4327,7 @@ mysql_get_server_version(MYSQL *mysql) */ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) { - struct charset_info_st *cs; + CHARSET_INFO *cs; const char *save_csdir= charsets_dir; if (mysql->options.charset_dir) @@ -4310,7 +4442,7 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) 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; } @@ -4328,19 +4460,11 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) DBUG_RETURN(CR_OK); } -/** - The main function of the mysql_clear_password authentication plugin. -*/ -static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +my_socket STDCALL +mysql_get_socket(const MYSQL *mysql) { - int res; - - /* send password in clear text */ - res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, - strlen(mysql->passwd) + 1); - - return res ? CR_ERROR : CR_OK; + 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 4016f0744be..f31ddb22a6a 100644 --- a/sql-common/client_plugin.c +++ b/sql-common/client_plugin.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. +/* 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 @@ -27,6 +28,11 @@ There is no reference counting and no unloading either. */ +#if _MSC_VER +/* Silence warnings about variable 'unused' being used. */ +#define FORCE_INIT_OF_VARS 1 +#endif + #include <my_global.h> #include "mysql.h" #include <my_sys.h> @@ -46,7 +52,8 @@ struct st_client_plugin_int { static my_bool initialized= 0; static MEM_ROOT mem_root; -static const char *plugin_declarations_sym= "_mysql_client_plugin_declaration_"; +#define plugin_declarations_sym "_mysql_client_plugin_declaration_" + static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= { 0, /* these two are taken by Connector/C */ @@ -67,13 +74,15 @@ static pthread_mutex_t LOCK_load_client_plugin; 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); } /** @@ -90,18 +99,19 @@ 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); } /** @@ -123,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); @@ -165,7 +176,7 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, plugin_list[plugin->type]= p; net_clear_error(&mysql->net); - return plugin; + DBUG_RETURN(plugin); err2: if (plugin->deinit) @@ -175,8 +186,8 @@ err1: ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name, errmsg); if (dlhandle) - dlclose(dlhandle); - return NULL; + (void)dlclose(dlhandle); + DBUG_RETURN(NULL); } /** @@ -197,10 +208,11 @@ err1: 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; + if (!s) + DBUG_VOID_RETURN; free_env= plugs= my_strdup(s, MYF(MY_WME)); @@ -212,6 +224,7 @@ static void load_env_plugins(MYSQL *mysql) } while (s); my_free(free_env); + DBUG_VOID_RETURN; } /********** extern functions to be used by libmysql *********************/ @@ -228,9 +241,12 @@ 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 */ @@ -244,13 +260,13 @@ int mysql_client_plugin_init() pthread_mutex_lock(&LOCK_load_client_plugin); for (builtin= mysql_client_builtins; *builtin; builtin++) - add_plugin(&mysql, *builtin, 0, 0, 0); + add_plugin(&mysql, *builtin, 0, 0, unused); pthread_mutex_unlock(&LOCK_load_client_plugin); load_env_plugins(&mysql); - return 0; + DBUG_RETURN(0); } /** @@ -262,9 +278,10 @@ 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) @@ -272,13 +289,14 @@ void mysql_client_plugin_deinit() if (p->plugin->deinit) p->plugin->deinit(); if (p->dlhandle) - dlclose(p->dlhandle); + (void)dlclose(p->dlhandle); } bzero(&plugin_list, sizeof(plugin_list)); initialized= 0; free_root(&mem_root, MYF(0)); pthread_mutex_destroy(&LOCK_load_client_plugin); + DBUG_VOID_RETURN; } /************* public facing functions, for client consumption *********/ @@ -288,8 +306,12 @@ struct st_mysql_client_plugin * 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); @@ -302,10 +324,10 @@ mysql_client_register_plugin(MYSQL *mysql, plugin= NULL; } else - plugin= add_plugin(mysql, plugin, 0, 0, 0); + 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 */ @@ -317,11 +339,8 @@ 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; -#ifdef _WIN32 - char win_errormsg[2048]; -#endif + DBUG_ENTER("mysql_load_plugin_v"); - 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)) { @@ -348,36 +367,15 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, /* Open new dll handle */ if (!(dlhandle= dlopen(dlpath, RTLD_NOW))) { -#if defined(__APPLE__) - /* Apple supports plugins with .so also, so try this as well */ - strxnmov(dlpath, sizeof(dlpath) - 1, - mysql->options.extension && mysql->options.extension->plugin_dir ? - mysql->options.extension->plugin_dir : PLUGINDIR, "/", - name, ".so", NullS); - if ((dlhandle= dlopen(dlpath, RTLD_NOW))) - goto have_plugin; -#endif - DBUG_PRINT ("info", ("failed to dlopen")); -#ifdef _WIN32 - if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - 0, GetLastError(), 0, win_errormsg, 2048, NULL)) - errmsg= win_errormsg; - else - errmsg= ""; -#else errmsg= dlerror(); -#endif goto err; } -#if defined(__APPLE__) -have_plugin: -#endif if (!(sym= dlsym(dlhandle, plugin_declarations_sym))) { errmsg= "not a plugin"; - dlclose(dlhandle); + (void)dlclose(dlhandle); goto err; } @@ -422,10 +420,12 @@ 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 */ @@ -433,8 +433,8 @@ 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_ENTER ("mysql_client_find_plugin"); DBUG_PRINT ("entry", ("name=%s, type=%d", name, type)); if (is_not_initialized(mysql, name)) DBUG_RETURN (NULL); diff --git a/sql-common/my_time.c b/sql-common/my_time.c index e42f5f18a6d..df75bb49c89 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. +/* + Copyright (c) 2004, 2012, 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 @@ -18,6 +19,7 @@ #include <m_ctype.h> /* Windows version of localtime_r() is declared in my_ptrhead.h */ #include <my_pthread.h> +#include <mysqld_error.h> ulonglong log_10_int[20]= { @@ -81,7 +83,7 @@ my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, if (not_zero_date) { if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) && - (ltime->month == 0 || ltime->day == 0)) || + (ltime->month == 0 || ltime->day == 0)) || ltime->neg || (!(flags & TIME_INVALID_DATES) && ltime->month && ltime->day > days_in_month[ltime->month-1] && (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 || @@ -160,7 +162,7 @@ enum enum_mysql_timestamp_type str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, 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; @@ -171,9 +173,14 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, my_bool found_delimitier= 0, found_space= 0; uint frac_pos, frac_len; DBUG_ENTER("str_to_datetime"); - DBUG_PRINT("ENTER",("str: %.*s",length,str)); + DBUG_PRINT("enter",("str: %.*s",length,str)); - LINT_INIT(field_length); + if (flags & TIME_TIME_ONLY) + { + enum enum_mysql_timestamp_type ret; + ret= str_to_time(str, length, l_time, flags, was_cut); + DBUG_RETURN(ret); + } *was_cut= 0; @@ -337,7 +344,7 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, { if (str[0] == 'p' || str[0] == 'P') add_hours= 12; - else if (str[0] != 'a' || str[0] != 'A') + else if (str[0] != 'a' && str[0] != 'A') continue; /* Not AM/PM */ str+= 2; /* Skip AM/PM */ /* Skip space after AM/PM */ @@ -477,12 +484,13 @@ err: work with times where the time arguments are in the above order. RETURN - 0 ok - 1 error + MYSQL_TIMESTAMP_TIME + MYSQL_TIMESTAMP_ERROR */ -my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - int *warning) +enum enum_mysql_timestamp_type +str_to_time(const char *str, uint length, MYSQL_TIME *l_time, + ulonglong fuzzydate, int *warning) { ulong date[5]; ulonglong value; @@ -501,7 +509,7 @@ my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, length--; } if (str == end) - return 1; + return MYSQL_TIMESTAMP_ERROR; /* Check first if this is a full TIMESTAMP */ if (length >= 12) @@ -509,12 +517,13 @@ my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int was_cut; enum enum_mysql_timestamp_type res= str_to_datetime(str, length, l_time, - (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut); + (fuzzydate & ~TIME_TIME_ONLY) | TIME_DATETIME_ONLY, + &was_cut); if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR) { if (was_cut) *warning|= MYSQL_TIME_WARN_TRUNCATED; - return res == MYSQL_TIMESTAMP_ERROR; + return res; } } @@ -609,7 +618,7 @@ fractional: ((str[1] == '-' || str[1] == '+') && (end - str) > 2 && my_isdigit(&my_charset_latin1, str[2])))) - return 1; + return MYSQL_TIMESTAMP_ERROR; if (internal_format_positions[7] != 255) { @@ -632,7 +641,7 @@ fractional: if (date[0] > UINT_MAX || date[1] > UINT_MAX || date[2] > UINT_MAX || date[3] > UINT_MAX || date[4] > UINT_MAX) - return 1; + return MYSQL_TIMESTAMP_ERROR; l_time->year= 0; /* For protocol::store_time */ l_time->month= 0; @@ -644,8 +653,8 @@ fractional: l_time->time_type= MYSQL_TIMESTAMP_TIME; /* Check if the value is valid and fits into MYSQL_TIME range */ - if (check_time_range(l_time, warning)) - return 1; + if (check_time_range(l_time, 6, warning)) + return MYSQL_TIMESTAMP_ERROR; /* Check if there is garbage at end of the MYSQL_TIME specification */ if (str != end) @@ -659,7 +668,7 @@ fractional: } } while (++str != end); } - return 0; + return MYSQL_TIMESTAMP_TIME; } @@ -669,6 +678,7 @@ fractional: SYNOPSIS: check_time_range() time pointer to MYSQL_TIME value + uint dec warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range DESCRIPTION @@ -681,24 +691,31 @@ fractional: 1 time value is invalid */ -int check_time_range(struct st_mysql_time *my_time, int *warning) +int check_time_range(struct st_mysql_time *my_time, uint dec, int *warning) { longlong hour; + static ulong max_sec_part[TIME_SECOND_PART_DIGITS+1]= {000000, 900000, 990000, + 999000, 999900, 999990, 999999}; if (my_time->minute >= 60 || my_time->second >= 60) return 1; hour= my_time->hour + (24*my_time->day); + + if (dec == AUTO_SEC_PART_DIGITS) + dec= TIME_SECOND_PART_DIGITS; + if (hour <= TIME_MAX_HOUR && (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE || - my_time->second != TIME_MAX_SECOND || !my_time->second_part)) + my_time->second != TIME_MAX_SECOND || + my_time->second_part <= max_sec_part[dec])) return 0; my_time->day= 0; my_time->hour= TIME_MAX_HOUR; my_time->minute= TIME_MAX_MINUTE; my_time->second= TIME_MAX_SECOND; - my_time->second_part= 0; + my_time->second_part= max_sec_part[dec]; *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE; return 0; } @@ -715,7 +732,7 @@ void my_init_time(void) time_t seconds; struct tm *l_time,tm_tmp; MYSQL_TIME my_time; - my_bool not_used; + uint not_used; seconds= (time_t) time((time_t*) 0); localtime_r(&seconds,&tm_tmp); @@ -727,6 +744,10 @@ void my_init_time(void) my_time.hour= (uint) l_time->tm_hour; my_time.minute= (uint) l_time->tm_min; my_time.second= (uint) l_time->tm_sec; + my_time.neg= 0; + my_time.second_part= 0; + my_time.time_type= MYSQL_TIMESTAMP_DATETIME; + my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */ } @@ -797,7 +818,11 @@ long calc_daynr(uint year,uint month,uint day) t - time value to be converted my_timezone - pointer to long where offset of system time zone from UTC will be stored for caching - in_dst_time_gap - set to true if time falls into spring time-gap + error_code - 0, if the conversion was successful; + ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value + which is out of TIMESTAMP range; + ER_WARN_INVALID_TIMESTAMP, if t represents value which + doesn't exists (falls into the spring time-gap). NOTES The idea is to cache the time zone offset from UTC (including daylight @@ -811,8 +836,7 @@ long calc_daynr(uint year,uint month,uint day) Time in UTC seconds since Unix Epoch representation. */ my_time_t -my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, - my_bool *in_dst_time_gap) +my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, uint *error_code) { uint loop; time_t tmp= 0; @@ -829,7 +853,11 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME)); if (!validate_timestamp_range(t)) + { + *error_code= ER_WARN_DATA_OUT_OF_RANGE; return 0; + } + *error_code= 0; /* Calculate the gmt time based on current time and timezone @@ -975,7 +1003,7 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, else if (diff == -3600) tmp-=t->minute*60 + t->second; /* Move to previous hour */ - *in_dst_time_gap= 1; + *error_code= ER_WARN_INVALID_TIMESTAMP; } *my_timezone= current_timezone; @@ -994,7 +1022,10 @@ my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone, larger then TIMESTAMP_MAX_VALUE, so another check will work. */ if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp)) + { tmp= 0; + *error_code= ER_WARN_DATA_OUT_OF_RANGE; + } return (my_time_t) tmp; } /* my_system_gmt_sec */ @@ -1010,36 +1041,117 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type) /* + Helper function for datetime formatting. + Format number as string, left-padded with 0. + + The reason to use own formatting rather than sprintf() is performance - in a + datetime benchmark it helped to reduced the datetime formatting overhead + from ~30% down to ~4%. +*/ + +static char* fmt_number(uint val, char *out, uint digits) +{ + uint i; + for(i= 0; i < digits; i++) + { + out[digits-i-1]= '0' + val%10; + val/=10; + } + return out + digits; +} + + +/* Functions to convert time/date/datetime value to a string, using default format. This functions don't check that given MYSQL_TIME structure members are in valid range. If they are not, return value won't reflect any - valid date either. Additionally, make_time doesn't take into - account time->day member: it's assumed that days have been converted - to hours already. + valid date either. RETURN number of characters written to 'to' */ -int my_time_to_str(const MYSQL_TIME *l_time, char *to) +int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { - uint extra_hours= 0; - return sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""), - extra_hours+ l_time->hour, l_time->minute, l_time->second); + uint day= (l_time->year || l_time->month) ? 0 : l_time->day; + uint hour= day * 24 + l_time->hour; + char*pos= to; + + if (digits == AUTO_SEC_PART_DIGITS) + digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; + + DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); + + if(l_time->neg) + *pos++= '-'; + + if(hour > 99) + /* Need more than 2 digits for hours in string representation. */ + pos= longlong10_to_str((longlong)hour, pos, 10); + else + pos= fmt_number(hour, pos, 2); + + *pos++= ':'; + pos= fmt_number(l_time->minute, pos, 2); + *pos++= ':'; + pos= fmt_number(l_time->second, pos, 2); + + if (digits) + { + *pos++= '.'; + pos= fmt_number((uint)sec_part_shift(l_time->second_part, digits), + pos, digits); + } + + *pos= 0; + return (int) (pos-to); } + int my_date_to_str(const MYSQL_TIME *l_time, char *to) { - return sprintf(to, "%04u-%02u-%02u", - l_time->year, l_time->month, l_time->day); + char *pos=to; + pos= fmt_number(l_time->year, pos, 4); + *pos++='-'; + pos= fmt_number(l_time->month, pos, 2); + *pos++='-'; + pos= fmt_number(l_time->day, pos, 2); + *pos= 0; + return (int)(pos - to); } -int my_datetime_to_str(const MYSQL_TIME *l_time, char *to) + +int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { - return sprintf(to, "%04u-%02u-%02u %02u:%02u:%02u", - l_time->year, l_time->month, l_time->day, - l_time->hour, l_time->minute, l_time->second); + char *pos= to; + + if (digits == AUTO_SEC_PART_DIGITS) + digits= l_time->second_part ? TIME_SECOND_PART_DIGITS : 0; + + DBUG_ASSERT(digits <= TIME_SECOND_PART_DIGITS); + + pos= fmt_number(l_time->year, pos, 4); + *pos++='-'; + pos= fmt_number(l_time->month, pos, 2); + *pos++='-'; + pos= fmt_number(l_time->day, pos, 2); + *pos++=' '; + pos= fmt_number(l_time->hour, pos, 2); + *pos++= ':'; + pos= fmt_number(l_time->minute, pos, 2); + *pos++= ':'; + pos= fmt_number(l_time->second, pos, 2); + + if (digits) + { + *pos++='.'; + pos= fmt_number((uint) sec_part_shift(l_time->second_part, digits), pos, + digits); + } + + *pos= 0; + return (int)(pos - to); } @@ -1050,19 +1162,22 @@ int my_datetime_to_str(const MYSQL_TIME *l_time, char *to) SYNOPSIS my_TIME_to_string() + RETURN + length of string + NOTE The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved. */ -int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) +int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint digits) { switch (l_time->time_type) { case MYSQL_TIMESTAMP_DATETIME: - return my_datetime_to_str(l_time, to); + return my_datetime_to_str(l_time, to, digits); case MYSQL_TIMESTAMP_DATE: return my_date_to_str(l_time, to); case MYSQL_TIMESTAMP_TIME: - return my_time_to_str(l_time, to); + return my_time_to_str(l_time, to, digits); case MYSQL_TIMESTAMP_NONE: case MYSQL_TIMESTAMP_ERROR: to[0]='\0'; @@ -1100,16 +1215,15 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) Datetime value in YYYYMMDDHHMMSS format. */ -longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, +longlong number_to_datetime(longlong nr, ulong sec_part, MYSQL_TIME *time_res, ulonglong flags, int *was_cut) { long part1,part2; *was_cut= 0; - bzero((char*) time_res, sizeof(*time_res)); time_res->time_type=MYSQL_TIMESTAMP_DATE; - if (nr == LL(0) || nr >= LL(10000101000000)) + if (nr == 0 || nr >= 10000101000000LL || sec_part) { time_res->time_type=MYSQL_TIMESTAMP_DATETIME; goto ok; @@ -1164,22 +1278,87 @@ longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, time_res->hour= (int) (part2/10000L); part2%=10000L; time_res->minute=(int) part2 / 100; time_res->second=(int) part2 % 100; + time_res->second_part= sec_part; + time_res->neg= 0; if (time_res->year <= 9999 && time_res->month <= 12 && time_res->day <= 31 && time_res->hour <= 23 && time_res->minute <= 59 && time_res->second <= 59 && - !check_date(time_res, (nr != 0), flags, was_cut)) + sec_part <= TIME_MAX_SECOND_PART && + !check_date(time_res, nr || sec_part, flags, was_cut)) return nr; /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */ - if (!nr && (flags & TIME_NO_ZERO_DATE)) - return LL(-1); + if (nr || !(flags & TIME_NO_ZERO_DATE)) + *was_cut= 1; + return LL(-1); err: - *was_cut= 1; + { + /* reset everything except time_type */ + enum enum_mysql_timestamp_type save= time_res->time_type; + bzero((char*) time_res, sizeof(*time_res)); + time_res->time_type= save; /* Restore range */ + *was_cut= 1; /* Found invalid date */ + } return LL(-1); } +/* + Convert a pair of integers to a MYSQL_TIME struct. + + @param[in] nr a number to convert + @param[out] ltime Date to check. + @param[out] was_cut MYSQL_TIME_WARN_OUT_OF_RANGE if the value was + modified to fit in the valid range. Otherwise 0. + + @details + Takes a number in the [-]HHHMMSS.uuuuuu, + YYMMDDHHMMSS.uuuuuu, or in the YYYYMMDDHHMMSS.uuuuuu formats. + + @return + 0 time value is valid, but was possibly truncated + -1 time value is invalid +*/ +int number_to_time(my_bool neg, longlong nr, ulong sec_part, + MYSQL_TIME *ltime, int *was_cut) +{ + if (nr > 9999999 && neg == 0) + { + if (number_to_datetime(nr, sec_part, ltime, + TIME_FUZZY_DATE | TIME_INVALID_DATES, was_cut) < 0) + return -1; + + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + *was_cut= MYSQL_TIME_WARN_TRUNCATED; + return 0; + } + + *was_cut= 0; + ltime->year= ltime->month= ltime->day= 0; + ltime->time_type= MYSQL_TIMESTAMP_TIME; + + ltime->neg= neg; + + if (nr > TIME_MAX_VALUE) + { + nr= TIME_MAX_VALUE; + sec_part= TIME_MAX_SECOND_PART; + *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE; + } + ltime->hour = (uint)(nr/100/100); + ltime->minute= nr/100%100; + ltime->second= nr%100; + ltime->second_part= sec_part; + + if (ltime->minute < 60 && ltime->second < 60 && sec_part <= TIME_MAX_SECOND_PART) + return 0; + + *was_cut= MYSQL_TIME_WARN_TRUNCATED; + return -1; +} + /* Convert time value to integer in YYYYMMDDHHMMSS format */ @@ -1228,7 +1407,7 @@ ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time) DESCRIPTION The function is used when we need to convert value of time item to a number if it's used in numeric context, i. e.: - SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0; + SELECT NOW()+1, CURDATE()+0, CURTIME()+0; SELECT ?+1; NOTE @@ -1255,3 +1434,41 @@ ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time) return 0; } +double TIME_to_double(const MYSQL_TIME *my_time) +{ + double d= (double)TIME_to_ulonglong(my_time); + + if (my_time->time_type == MYSQL_TIMESTAMP_DATE) + return d; + + d+= my_time->second_part/(double)TIME_SECOND_PART_FACTOR; + return my_time->neg ? -d : d; +} + +longlong pack_time(MYSQL_TIME *my_time) +{ + return ((((((my_time->year * 13ULL + + my_time->month) * 32ULL + + my_time->day) * 24ULL + + my_time->hour) * 60ULL + + my_time->minute) * 60ULL + + my_time->second) * 1000000ULL + + my_time->second_part) * (my_time->neg ? -1 : 1); +} + +#define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR + +MYSQL_TIME *unpack_time(longlong packed, MYSQL_TIME *my_time) +{ + if ((my_time->neg= packed < 0)) + packed= -packed; + get_one(my_time->second_part, 1000000ULL); + get_one(my_time->second, 60ULL); + get_one(my_time->minute, 60ULL); + get_one(my_time->hour, 24ULL); + get_one(my_time->day, 32ULL); + get_one(my_time->month, 13ULL); + my_time->year= (uint)packed; + my_time->time_type= MYSQL_TIMESTAMP_DATETIME; + return my_time; +} 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() +*/ |