diff options
author | unknown <knielsen@knielsen-hq.org> | 2012-02-21 22:15:44 +0100 |
---|---|---|
committer | unknown <knielsen@knielsen-hq.org> | 2012-02-21 22:15:44 +0100 |
commit | f6b68a10707c95e24f67d338eaaa03f1bd575598 (patch) | |
tree | bd7748f0d0a239145e4c02b223f523b4c571aacb /sql-common | |
parent | 4b9907979ce316be12a7ce2b62874b42edf2e39e (diff) | |
parent | 7c8ebb532eef543a9b98107c164a12a49e28d0ca (diff) | |
download | mariadb-git-f6b68a10707c95e24f67d338eaaa03f1bd575598.tar.gz |
Merge MWL#192: Non-blocking client library, into MariaDB 5.5.
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/client.c | 191 | ||||
-rw-r--r-- | sql-common/mysql_async.c | 1947 |
2 files changed, 2111 insertions, 27 deletions
diff --git a/sql-common/client.c b/sql-common/client.c index 3e9a4592853..2904ffa7b64 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -112,6 +112,8 @@ my_bool net_flush(NET *net); #include "client_settings.h" #include <sql_common.h> #include <mysql/client_plugin.h> +#include <my_context.h> +#include <mysql_async.h> #define native_password_plugin_name "mysql_native_password" #define old_password_plugin_name "mysql_old_password" @@ -2939,6 +2941,22 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, } +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, @@ -3097,9 +3115,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)); @@ -3214,9 +3231,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 @@ -3232,7 +3258,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", @@ -3254,15 +3281,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)); @@ -3273,6 +3291,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); @@ -3492,16 +3513,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)); @@ -3518,10 +3562,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); @@ -3532,11 +3597,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; @@ -3615,8 +3684,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)); @@ -3724,6 +3799,30 @@ void mysql_detach_stmt_list(LIST **stmt_list __attribute__((unused)), (As some clients call this after mysql_real_connect() fails) */ +/* + mysql_close() can actually block, at least in theory, if the socket buffer + is full when sending the COM_QUIT command. + + On the other hand, the latter part of mysql_close() needs to free the stack + used for non-blocking operation of blocking stuff, so that later part can + _not_ be done non-blocking. + + Therefore, mysql_close_slow_part() is used to run the parts of mysql_close() + that may block. It can be called before mysql_close(), and in that case + mysql_close() is guaranteed not to need to block. */ +void STDCALL mysql_close_slow_part(MYSQL *mysql) +{ + /* If connection is still up, send a QUIT message */ + if (mysql->net.vio != 0) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + mysql->reconnect=0; + simple_command(mysql,COM_QUIT,(uchar*) 0,0,1); + end_server(mysql); /* Sets mysql->net.vio= 0 */ + } +} + void STDCALL mysql_close(MYSQL *mysql) { DBUG_ENTER("mysql_close"); @@ -3731,15 +3830,7 @@ void STDCALL mysql_close(MYSQL *mysql) if (mysql) /* Some simple safety */ { - /* If connection is still up, send a QUIT message */ - if (mysql->net.vio != 0) - { - free_old_query(mysql); - mysql->status=MYSQL_STATUS_READY; /* Force command */ - mysql->reconnect=0; - simple_command(mysql,COM_QUIT,(uchar*) 0,0,1); - end_server(mysql); /* Sets mysql->net.vio= 0 */ - } + mysql_close_slow_part(mysql); mysql_close_free_options(mysql); mysql_close_free(mysql); mysql_detach_stmt_list(&mysql->stmts, "mysql_close"); @@ -4025,9 +4116,14 @@ 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) { + struct mysql_async_context *ctxt; + size_t stacksize; + DBUG_ENTER("mysql_options"); DBUG_PRINT("enter",("option: %d",(int) option)); switch (option) { @@ -4120,6 +4216,39 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) mysql->options.extension->report_progress= (void (*)(const MYSQL *, uint, uint, double, const char *, uint)) arg; break; + case MYSQL_OPT_NONBLOCK: + if (mysql->options.extension && + (ctxt = mysql->options.extension->async_context) != 0) + { + /* + We must not allow changing the stack size while a non-blocking call is + suspended (as the stack is then in use). + */ + if (ctxt->suspended) + DBUG_RETURN(1); + my_context_destroy(&ctxt->async_context); + my_free(ctxt); + } + if (!(ctxt= (struct mysql_async_context *) + my_malloc(sizeof(*ctxt), MYF(MY_ZEROFILL)))) + { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + DBUG_RETURN(1); + } + stacksize= 0; + if (arg) + stacksize= *(const size_t *)arg; + if (!stacksize) + stacksize= ASYNC_CONTEXT_DEFAULT_STACK_SIZE; + if (my_context_init(&ctxt->async_context, stacksize)) + { + my_free(ctxt); + DBUG_RETURN(1); + } + EXTENSION_SET(&(mysql->options), async_context, ctxt) + if (mysql->net.vio) + mysql->net.vio->async_context= ctxt; + break; default: DBUG_RETURN(1); } @@ -4329,3 +4458,11 @@ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) DBUG_RETURN(CR_OK); } + +my_socket STDCALL +mysql_get_socket(const MYSQL *mysql) +{ + if (mysql->net.vio) + return mysql->net.vio->sd; + return INVALID_SOCKET; +} diff --git a/sql-common/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() +*/ |