1 /*
2   Copyright 2011 Kristian Nielsen and Monty Program Ab
3 
4   This file is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License as published by the Free Software Foundation; either
7   version 2.1 of the License, or (at your option) any later version.
8 
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 /*
19   Wrappers that re-implement the normal blocking libmysql API calls in terms
20   of the non-blocking API calls and explicit waiting.
21 
22   Used to test the non-blocking calls using mysql_client_test.
23 */
24 
25 #ifndef __WIN__
26 #include <poll.h>
27 #else
28 #include <WinSock2.h>
29 #endif
30 
31 /*
32   Run the appropriate poll() syscall to wait for the event that libmysql
33   requested. Return which event(s) occurred.
34 */
35 static int
wait_for_mysql(MYSQL * mysql,int status)36 wait_for_mysql(MYSQL *mysql, int status)
37 {
38 #ifdef __WIN__
39   fd_set rs, ws, es;
40   int res;
41   struct timeval tv, *timeout;
42   my_socket s= mysql_get_socket(mysql);
43   FD_ZERO(&rs);
44   FD_ZERO(&ws);
45   FD_ZERO(&es);
46   if (status & MYSQL_WAIT_READ)
47     FD_SET(s, &rs);
48   if (status & MYSQL_WAIT_WRITE)
49     FD_SET(s, &ws);
50   if (status & MYSQL_WAIT_EXCEPT)
51     FD_SET(s, &es);
52   if (status & MYSQL_WAIT_TIMEOUT)
53   {
54     tv.tv_sec= mysql_get_timeout_value(mysql);
55     tv.tv_usec= 0;
56     timeout= &tv;
57   }
58   else
59     timeout= NULL;
60   res= select(1, &rs, &ws, &es, timeout);
61   if (res == 0)
62     return MYSQL_WAIT_TIMEOUT;
63   else if (res == SOCKET_ERROR)
64     return MYSQL_WAIT_TIMEOUT;
65   else
66   {
67     int status= 0;
68     if (FD_ISSET(s, &rs))
69       status|= MYSQL_WAIT_READ;
70     if (FD_ISSET(s, &ws))
71       status|= MYSQL_WAIT_WRITE;
72     if (FD_ISSET(s, &es))
73       status|= MYSQL_WAIT_EXCEPT;
74     return status;
75   }
76 #else
77   struct pollfd pfd;
78   int timeout;
79   int res;
80 
81   pfd.fd= mysql_get_socket(mysql);
82   pfd.events=
83     (status & MYSQL_WAIT_READ ? POLLIN : 0) |
84     (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) |
85     (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0);
86   if (status & MYSQL_WAIT_TIMEOUT)
87     timeout= 1000*mysql_get_timeout_value(mysql);
88   else
89     timeout= -1;
90   do {
91     res= poll(&pfd, 1, timeout);
92     /*
93       In a real event framework, we should re-compute the timeout on getting
94       EINTR to account for the time elapsed before the interruption.
95     */
96   } while (res < 0 && errno == EINTR);
97   if (res == 0)
98     return MYSQL_WAIT_TIMEOUT;
99   else if (res < 0)
100     return MYSQL_WAIT_TIMEOUT;
101   else
102   {
103     int status= 0;
104     if (pfd.revents & POLLIN)
105       status|= MYSQL_WAIT_READ;
106     if (pfd.revents & POLLOUT)
107       status|= MYSQL_WAIT_WRITE;
108     if (pfd.revents & POLLPRI)
109       status|= MYSQL_WAIT_EXCEPT;
110     return status;
111   }
112 #endif
113 }
114 
115 
116 /*
117   If WRAP_NONBLOCK_ENABLED is defined, it is a variable that can be used to
118   enable or disable the use of non-blocking API wrappers. If true the
119   non-blocking API will be used, if false the normal blocking API will be
120   called directly.
121 */
122 #ifdef WRAP_NONBLOCK_ENABLED
123 #define USE_BLOCKING(name__, invoke_blocking__) \
124   if (!(WRAP_NONBLOCK_ENABLED)) return name__ invoke_blocking__;
125 #define USE_BLOCKING_VOID_RETURN(name__, invoke__) \
126   if (!(WRAP_NONBLOCK_ENABLED)) { name__ invoke__; return; }
127 #else
128 #define USE_BLOCKING(name__, invoke_blocking__)
129 #define USE_BLOCKING_VOID_RETURN(name__, invoke__)
130 #endif
131 
132 /*
133   I would preferably have declared the wrappers static.
134   However, if we do so, compilers will warn about definitions not used, and
135   with -Werror this breaks compilation :-(
136 */
137 #define MK_WRAPPER(ret_type__, name__, decl__, invoke__, invoke_blocking__, cont_arg__, mysql__) \
138 ret_type__ wrap_ ## name__ decl__                                             \
139 {                                                                             \
140   ret_type__ res;                                                             \
141   int status;                                                                 \
142   USE_BLOCKING(name__, invoke_blocking__)                                     \
143   status= name__ ## _start invoke__;                                          \
144   while (status)                                                              \
145   {                                                                           \
146     status= wait_for_mysql(mysql__, status);                                  \
147     status= name__ ## _cont(&res, cont_arg__, status);                        \
148   }                                                                           \
149   return res;                                                                 \
150 }
151 
152 #define MK_WRAPPER_VOID_RETURN(name__, decl__, invoke__, cont_arg__, mysql__) \
153 void wrap_ ## name__ decl__                                                   \
154 {                                                                             \
155   int status;                                                                 \
156   USE_BLOCKING_VOID_RETURN(name__, invoke__)                                  \
157   status= name__ ## _start invoke__;                                          \
158   while (status)                                                              \
159   {                                                                           \
160     status= wait_for_mysql(mysql__, status);                                  \
161     status= name__ ## _cont(cont_arg__, status);                              \
162   }                                                                           \
163 }
164 
165 MK_WRAPPER(
166   MYSQL *,
167   mysql_real_connect,
168   (MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag),
169   (&res, mysql, host, user, passwd, db, port, unix_socket, clientflag),
170   (mysql, host, user, passwd, db, port, unix_socket, clientflag),
171   mysql,
172   mysql)
173 
174 
175 MK_WRAPPER(
176   int,
177   mysql_real_query,
178   (MYSQL *mysql, const char *stmt_str, unsigned long length),
179   (&res, mysql, stmt_str, length),
180   (mysql, stmt_str, length),
181   mysql,
182   mysql)
183 
184 MK_WRAPPER(
185   MYSQL_ROW,
186   mysql_fetch_row,
187   (MYSQL_RES *result),
188   (&res, result),
189   (result),
190   result,
191   result->handle)
192 
193 MK_WRAPPER(
194   int,
195   mysql_set_character_set,
196   (MYSQL *mysql, const char *csname),
197   (&res, mysql, csname),
198   (mysql, csname),
199   mysql,
200   mysql)
201 
202 MK_WRAPPER(
203   int,
204   mysql_select_db,
205   (MYSQL *mysql, const char *db),
206   (&res, mysql, db),
207   (mysql, db),
208   mysql,
209   mysql)
210 
211 MK_WRAPPER(
212   int,
213   mysql_send_query,
214   (MYSQL *mysql, const char *q, unsigned long length),
215   (&res, mysql, q, length),
216   (mysql, q, length),
217   mysql,
218   mysql)
219 
220 MK_WRAPPER(
221   MYSQL_RES *,
222   mysql_store_result,
223   (MYSQL *mysql),
224   (&res, mysql),
225   (mysql),
226   mysql,
227   mysql)
228 
229 MK_WRAPPER_VOID_RETURN(
230   mysql_free_result,
231   (MYSQL_RES *result),
232   (result),
233   result,
234   result->handle)
235 
236 MK_WRAPPER_VOID_RETURN(
237   mysql_close,
238   (MYSQL *sock),
239   (sock),
240   sock,
241   sock)
242 
243 MK_WRAPPER(
244   my_bool,
245   mysql_change_user,
246   (MYSQL *mysql, const char *user, const char *passwd, const char *db),
247   (&res, mysql, user, passwd, db),
248   (mysql, user, passwd, db),
249   mysql,
250   mysql)
251 
252 MK_WRAPPER(
253   int,
254   mysql_query,
255   (MYSQL *mysql, const char *q),
256   (&res, mysql, q),
257   (mysql, q),
258   mysql,
259   mysql)
260 
261 MK_WRAPPER(
262   int,
263   mysql_shutdown,
264   (MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level),
265   (&res, mysql, shutdown_level),
266   (mysql, shutdown_level),
267   mysql,
268   mysql)
269 
270 MK_WRAPPER(
271   int,
272   mysql_dump_debug_info,
273   (MYSQL *mysql),
274   (&res, mysql),
275   (mysql),
276   mysql,
277   mysql)
278 
279 MK_WRAPPER(
280   int,
281   mysql_refresh,
282   (MYSQL *mysql, unsigned int refresh_options),
283   (&res, mysql, refresh_options),
284   (mysql, refresh_options),
285   mysql,
286   mysql)
287 
288 MK_WRAPPER(
289   int,
290   mysql_kill,
291   (MYSQL *mysql, unsigned long pid),
292   (&res, mysql, pid),
293   (mysql, pid),
294   mysql,
295   mysql)
296 
297 MK_WRAPPER(
298   int,
299   mysql_set_server_option,
300   (MYSQL *mysql, enum enum_mysql_set_option option),
301   (&res, mysql, option),
302   (mysql, option),
303   mysql,
304   mysql)
305 
306 MK_WRAPPER(
307   int,
308   mysql_ping,
309   (MYSQL *mysql),
310   (&res, mysql),
311   (mysql),
312   mysql,
313   mysql)
314 
315 MK_WRAPPER(
316   const char *,
317   mysql_stat,
318   (MYSQL *mysql),
319   (&res, mysql),
320   (mysql),
321   mysql,
322   mysql)
323 
324 MK_WRAPPER(
325   my_bool,
326   mysql_read_query_result,
327   (MYSQL *mysql),
328   (&res, mysql),
329   (mysql),
330   mysql,
331   mysql)
332 
333 MK_WRAPPER(
334   int,
335   mysql_stmt_prepare,
336   (MYSQL_STMT *stmt, const char *query, unsigned long length),
337   (&res, stmt, query, length),
338   (stmt, query, length),
339   stmt,
340   stmt->mysql)
341 
342 MK_WRAPPER(
343   int,
344   mysql_stmt_execute,
345   (MYSQL_STMT *stmt),
346   (&res, stmt),
347   (stmt),
348   stmt,
349   stmt->mysql)
350 
351 MK_WRAPPER(
352   int,
353   mysql_stmt_fetch,
354   (MYSQL_STMT *stmt),
355   (&res, stmt),
356   (stmt),
357   stmt,
358   stmt->mysql)
359 
360 MK_WRAPPER(
361   int,
362   mysql_stmt_store_result,
363   (MYSQL_STMT *stmt),
364   (&res, stmt),
365   (stmt),
366   stmt,
367   stmt->mysql)
368 
369 MK_WRAPPER(
370   my_bool,
371   mysql_stmt_close,
372   (MYSQL_STMT *stmt),
373   (&res, stmt),
374   (stmt),
375   stmt,
376   stmt->mysql)
377 
378 MK_WRAPPER(
379   my_bool,
380   mysql_stmt_reset,
381   (MYSQL_STMT *stmt),
382   (&res, stmt),
383   (stmt),
384   stmt,
385   stmt->mysql)
386 
387 MK_WRAPPER(
388   my_bool,
389   mysql_stmt_free_result,
390   (MYSQL_STMT *stmt),
391   (&res, stmt),
392   (stmt),
393   stmt,
394   stmt->mysql)
395 
396 MK_WRAPPER(
397   my_bool,
398   mysql_stmt_send_long_data,
399   (MYSQL_STMT *stmt, unsigned int param_number, const char *data, unsigned long length),
400   (&res, stmt, param_number, data, length),
401   (stmt, param_number, data, length),
402   stmt,
403   stmt->mysql)
404 
405 MK_WRAPPER(
406   my_bool,
407   mysql_commit,
408   (MYSQL *mysql),
409   (&res, mysql),
410   (mysql),
411   mysql,
412   mysql)
413 
414 MK_WRAPPER(
415   my_bool,
416   mysql_rollback,
417   (MYSQL *mysql),
418   (&res, mysql),
419   (mysql),
420   mysql,
421   mysql)
422 
423 MK_WRAPPER(
424   my_bool,
425   mysql_autocommit,
426   (MYSQL *mysql, my_bool auto_mode),
427   (&res, mysql, auto_mode),
428   (mysql, auto_mode),
429   mysql,
430   mysql)
431 
432 MK_WRAPPER(
433   int,
434   mysql_next_result,
435   (MYSQL *mysql),
436   (&res, mysql),
437   (mysql),
438   mysql,
439   mysql)
440 
441 #undef USE_BLOCKING
442 #undef MK_WRAPPER
443 #undef MK_WRAPPER_VOID_RETURN
444 
445 
446 #define mysql_real_connect wrap_mysql_real_connect
447 #define mysql_real_query wrap_mysql_real_query
448 #define mysql_fetch_row wrap_mysql_fetch_row
449 #define mysql_set_character_set wrap_mysql_set_character_set
450 #define mysql_select_db wrap_mysql_select_db
451 #define mysql_send_query wrap_mysql_send_query
452 #define mysql_store_result wrap_mysql_store_result
453 #define mysql_free_result wrap_mysql_free_result
454 #define mysql_close wrap_mysql_close
455 #define mysql_change_user wrap_mysql_change_user
456 #define mysql_query wrap_mysql_query
457 #define mysql_shutdown wrap_mysql_shutdown
458 #define mysql_dump_debug_info wrap_mysql_dump_debug_info
459 #define mysql_refresh wrap_mysql_refresh
460 #define mysql_kill wrap_mysql_kill
461 #define mysql_set_server_option wrap_mysql_set_server_option
462 #define mysql_ping wrap_mysql_ping
463 #define mysql_stat wrap_mysql_stat
464 #define mysql_list_dbs wrap_mysql_list_dbs
465 #define mysql_list_tables wrap_mysql_list_tables
466 #define mysql_list_processes wrap_mysql_list_processes
467 #define mysql_read_query_result wrap_mysql_read_query_result
468 #define mysql_stmt_prepare wrap_mysql_stmt_prepare
469 #define mysql_stmt_execute wrap_mysql_stmt_execute
470 #define mysql_stmt_fetch wrap_mysql_stmt_fetch
471 #define mysql_stmt_store_result wrap_mysql_stmt_store_result
472 #define mysql_stmt_close wrap_mysql_stmt_close
473 #define mysql_stmt_reset wrap_mysql_stmt_reset
474 #define mysql_stmt_free_result wrap_mysql_stmt_free_result
475 #define mysql_stmt_send_long_data wrap_mysql_stmt_send_long_data
476 #define mysql_commit wrap_mysql_commit
477 #define mysql_rollback wrap_mysql_rollback
478 #define mysql_autocommit wrap_mysql_autocommit
479 #define mysql_next_result wrap_mysql_next_result
480