/* 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 . */ /* Wrappers that re-implement the normal blocking libmysql API calls in terms of the non-blocking API calls and explicit waiting. Used to test the non-blocking calls using mysql_client_test. */ #ifndef _WIN32 #include #else #include #endif /* Run the appropriate poll() syscall to wait for the event that libmysql requested. Return which event(s) occurred. */ static int wait_for_mysql(MYSQL *mysql, int status) { #ifdef _WIN32 fd_set rs, ws, es; int res; struct timeval tv, *timeout; my_socket s= mysql_get_socket(mysql); FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es); if (status & MYSQL_WAIT_READ) FD_SET(s, &rs); if (status & MYSQL_WAIT_WRITE) FD_SET(s, &ws); if (status & MYSQL_WAIT_EXCEPT) FD_SET(s, &es); if (status & MYSQL_WAIT_TIMEOUT) { tv.tv_sec= mysql_get_timeout_value(mysql); tv.tv_usec= 0; timeout= &tv; } else timeout= NULL; res= select(1, &rs, &ws, &es, timeout); if (res == 0) return MYSQL_WAIT_TIMEOUT; else if (res == SOCKET_ERROR) return MYSQL_WAIT_TIMEOUT; else { int status= 0; if (FD_ISSET(s, &rs)) status|= MYSQL_WAIT_READ; if (FD_ISSET(s, &ws)) status|= MYSQL_WAIT_WRITE; if (FD_ISSET(s, &es)) status|= MYSQL_WAIT_EXCEPT; return status; } #else struct pollfd pfd; int timeout; int res; pfd.fd= mysql_get_socket(mysql); pfd.events= (status & MYSQL_WAIT_READ ? POLLIN : 0) | (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) | (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0); if (status & MYSQL_WAIT_TIMEOUT) timeout= 1000*mysql_get_timeout_value(mysql); else timeout= -1; do { res= poll(&pfd, 1, timeout); /* In a real event framework, we should re-compute the timeout on getting EINTR to account for the time elapsed before the interruption. */ } while (res < 0 && errno == EINTR); if (res == 0) return MYSQL_WAIT_TIMEOUT; else if (res < 0) return MYSQL_WAIT_TIMEOUT; else { int status= 0; if (pfd.revents & POLLIN) status|= MYSQL_WAIT_READ; if (pfd.revents & POLLOUT) status|= MYSQL_WAIT_WRITE; if (pfd.revents & POLLPRI) status|= MYSQL_WAIT_EXCEPT; return status; } #endif } /* If WRAP_NONBLOCK_ENABLED is defined, it is a variable that can be used to enable or disable the use of non-blocking API wrappers. If true the non-blocking API will be used, if false the normal blocking API will be called directly. */ #ifdef WRAP_NONBLOCK_ENABLED #define USE_BLOCKING(name__, invoke_blocking__) \ if (!(WRAP_NONBLOCK_ENABLED)) return name__ invoke_blocking__; #define USE_BLOCKING_VOID_RETURN(name__, invoke__) \ if (!(WRAP_NONBLOCK_ENABLED)) { name__ invoke__; return; } #else #define USE_BLOCKING(name__, invoke_blocking__) #define USE_BLOCKING_VOID_RETURN(name__, invoke__) #endif /* I would preferably have declared the wrappers static. However, if we do so, compilers will warn about definitions not used, and with -Werror this breaks compilation :-( */ #define MK_WRAPPER(ret_type__, name__, decl__, invoke__, invoke_blocking__, cont_arg__, mysql__) \ ret_type__ wrap_ ## name__ decl__ \ { \ ret_type__ res; \ int status; \ USE_BLOCKING(name__, invoke_blocking__) \ status= name__ ## _start invoke__; \ while (status) \ { \ status= wait_for_mysql(mysql__, status); \ status= name__ ## _cont(&res, cont_arg__, status); \ } \ return res; \ } #define MK_WRAPPER_VOID_RETURN(name__, decl__, invoke__, cont_arg__, mysql__) \ void wrap_ ## name__ decl__ \ { \ int status; \ USE_BLOCKING_VOID_RETURN(name__, invoke__) \ status= name__ ## _start invoke__; \ while (status) \ { \ status= wait_for_mysql(mysql__, status); \ status= name__ ## _cont(cont_arg__, status); \ } \ } MK_WRAPPER( MYSQL *, mysql_real_connect, (MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag), (&res, mysql, host, user, passwd, db, port, unix_socket, clientflag), (mysql, host, user, passwd, db, port, unix_socket, clientflag), mysql, mysql) MK_WRAPPER( int, mysql_real_query, (MYSQL *mysql, const char *stmt_str, unsigned long length), (&res, mysql, stmt_str, length), (mysql, stmt_str, length), mysql, mysql) MK_WRAPPER( MYSQL_ROW, mysql_fetch_row, (MYSQL_RES *result), (&res, result), (result), result, result->handle) MK_WRAPPER( int, mysql_set_character_set, (MYSQL *mysql, const char *csname), (&res, mysql, csname), (mysql, csname), mysql, mysql) MK_WRAPPER( int, mysql_select_db, (MYSQL *mysql, const char *db), (&res, mysql, db), (mysql, db), mysql, mysql) MK_WRAPPER( int, mysql_send_query, (MYSQL *mysql, const char *q, unsigned long length), (&res, mysql, q, length), (mysql, q, length), mysql, mysql) MK_WRAPPER( MYSQL_RES *, mysql_store_result, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER_VOID_RETURN( mysql_free_result, (MYSQL_RES *result), (result), result, result->handle) MK_WRAPPER_VOID_RETURN( mysql_close, (MYSQL *sock), (sock), sock, sock) MK_WRAPPER( my_bool, mysql_change_user, (MYSQL *mysql, const char *user, const char *passwd, const char *db), (&res, mysql, user, passwd, db), (mysql, user, passwd, db), mysql, mysql) MK_WRAPPER( int, mysql_query, (MYSQL *mysql, const char *q), (&res, mysql, q), (mysql, q), mysql, mysql) MK_WRAPPER( int, mysql_shutdown, (MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level), (&res, mysql, shutdown_level), (mysql, shutdown_level), mysql, mysql) MK_WRAPPER( int, mysql_dump_debug_info, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( int, mysql_refresh, (MYSQL *mysql, unsigned int refresh_options), (&res, mysql, refresh_options), (mysql, refresh_options), mysql, mysql) MK_WRAPPER( int, mysql_kill, (MYSQL *mysql, unsigned long pid), (&res, mysql, pid), (mysql, pid), mysql, mysql) MK_WRAPPER( int, mysql_set_server_option, (MYSQL *mysql, enum enum_mysql_set_option option), (&res, mysql, option), (mysql, option), mysql, mysql) MK_WRAPPER( int, mysql_ping, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( const char *, mysql_stat, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( my_bool, mysql_read_query_result, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( int, mysql_stmt_prepare, (MYSQL_STMT *stmt, const char *query, unsigned long length), (&res, stmt, query, length), (stmt, query, length), stmt, stmt->mysql) MK_WRAPPER( int, mysql_stmt_execute, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( int, mysql_stmt_fetch, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( int, mysql_stmt_store_result, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( my_bool, mysql_stmt_close, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( my_bool, mysql_stmt_reset, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( my_bool, mysql_stmt_free_result, (MYSQL_STMT *stmt), (&res, stmt), (stmt), stmt, stmt->mysql) MK_WRAPPER( my_bool, mysql_stmt_send_long_data, (MYSQL_STMT *stmt, unsigned int param_number, const char *data, unsigned long length), (&res, stmt, param_number, data, length), (stmt, param_number, data, length), stmt, stmt->mysql) MK_WRAPPER( my_bool, mysql_commit, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( my_bool, mysql_rollback, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) MK_WRAPPER( my_bool, mysql_autocommit, (MYSQL *mysql, my_bool auto_mode), (&res, mysql, auto_mode), (mysql, auto_mode), mysql, mysql) MK_WRAPPER( int, mysql_next_result, (MYSQL *mysql), (&res, mysql), (mysql), mysql, mysql) #undef USE_BLOCKING #undef MK_WRAPPER #undef MK_WRAPPER_VOID_RETURN #define mysql_real_connect wrap_mysql_real_connect #define mysql_real_query wrap_mysql_real_query #define mysql_fetch_row wrap_mysql_fetch_row #define mysql_set_character_set wrap_mysql_set_character_set #define mysql_select_db wrap_mysql_select_db #define mysql_send_query wrap_mysql_send_query #define mysql_store_result wrap_mysql_store_result #define mysql_free_result wrap_mysql_free_result #define mysql_close wrap_mysql_close #define mysql_change_user wrap_mysql_change_user #define mysql_query wrap_mysql_query #define mysql_shutdown wrap_mysql_shutdown #define mysql_dump_debug_info wrap_mysql_dump_debug_info #define mysql_refresh wrap_mysql_refresh #define mysql_kill wrap_mysql_kill #define mysql_set_server_option wrap_mysql_set_server_option #define mysql_ping wrap_mysql_ping #define mysql_stat wrap_mysql_stat #define mysql_list_dbs wrap_mysql_list_dbs #define mysql_list_tables wrap_mysql_list_tables #define mysql_list_processes wrap_mysql_list_processes #define mysql_read_query_result wrap_mysql_read_query_result #define mysql_stmt_prepare wrap_mysql_stmt_prepare #define mysql_stmt_execute wrap_mysql_stmt_execute #define mysql_stmt_fetch wrap_mysql_stmt_fetch #define mysql_stmt_store_result wrap_mysql_stmt_store_result #define mysql_stmt_close wrap_mysql_stmt_close #define mysql_stmt_reset wrap_mysql_stmt_reset #define mysql_stmt_free_result wrap_mysql_stmt_free_result #define mysql_stmt_send_long_data wrap_mysql_stmt_send_long_data #define mysql_commit wrap_mysql_commit #define mysql_rollback wrap_mysql_rollback #define mysql_autocommit wrap_mysql_autocommit #define mysql_next_result wrap_mysql_next_result