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 #include "my_test.h"
18 #include "ma_common.h"
19 
20 
21 #ifndef _WIN32
22 #include <poll.h>
23 #else
24 #include <winsock2.h>
25 #endif
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <mysql.h>
30 
31 my_bool skip_async= 0;
32 
test_async(MYSQL * mysql)33 static int test_async(MYSQL *mysql)
34 {
35   int type;
36   mariadb_get_info(mysql, MARIADB_CONNECTION_PVIO_TYPE, &type);
37   if (type > MARIADB_CONNECTION_TCP)
38   {
39     skip_async= 1;
40     diag("Async IO not supported");
41   }
42   return OK;
43 }
44 
45 static int
wait_for_mysql(MYSQL * mysql,int status)46 wait_for_mysql(MYSQL *mysql, int status)
47 {
48 #ifdef _WIN32
49   fd_set rs, ws, es;
50   int res;
51   struct timeval tv, *timeout;
52   my_socket s= mysql_get_socket(mysql);
53   FD_ZERO(&rs);
54   FD_ZERO(&ws);
55   FD_ZERO(&es);
56   if (status & MYSQL_WAIT_READ)
57     FD_SET(s, &rs);
58   if (status & MYSQL_WAIT_WRITE)
59     FD_SET(s, &ws);
60   if (status & MYSQL_WAIT_EXCEPT)
61     FD_SET(s, &es);
62   if (status & MYSQL_WAIT_TIMEOUT)
63   {
64     tv.tv_sec= mysql_get_timeout_value(mysql);
65     tv.tv_usec= 0;
66     timeout= &tv;
67   }
68   else
69     timeout= NULL;
70   res= select(1, &rs, &ws, &es, timeout);
71   if (res == 0)
72     return MYSQL_WAIT_TIMEOUT;
73   else if (res == SOCKET_ERROR)
74   {
75     /*
76       In a real event framework, we should handle errors and re-try the select.
77     */
78     return MYSQL_WAIT_TIMEOUT;
79   }
80   else
81   {
82     int status= 0;
83     if (FD_ISSET(s, &rs))
84       status|= MYSQL_WAIT_READ;
85     if (FD_ISSET(s, &ws))
86       status|= MYSQL_WAIT_WRITE;
87     if (FD_ISSET(s, &es))
88       status|= MYSQL_WAIT_EXCEPT;
89     return status;
90   }
91 #else
92   struct pollfd pfd;
93   int timeout;
94   int res= -1;
95 
96   pfd.fd= mysql_get_socket(mysql);
97   pfd.events=
98     (status & MYSQL_WAIT_READ ? POLLIN : 0) |
99     (status & MYSQL_WAIT_WRITE ? POLLOUT : 0) |
100     (status & MYSQL_WAIT_EXCEPT ? POLLPRI : 0);
101   if (status & MYSQL_WAIT_TIMEOUT)
102   {
103     timeout= mysql_get_timeout_value_ms(mysql);
104   }
105   else
106     timeout= -1;
107   do {
108     res= poll(&pfd, 1, timeout);
109   } while (res == -1 && errno == EINTR);
110   if (res == 0)
111     return MYSQL_WAIT_TIMEOUT;
112   else if (res < 0)
113   {
114     /*
115       In a real event framework, we should handle EINTR and re-try the poll.
116     */
117     return MYSQL_WAIT_TIMEOUT;
118   }
119   else
120   {
121     int status= 0;
122     if (pfd.revents & POLLIN)
123       status|= MYSQL_WAIT_READ;
124     if (pfd.revents & POLLOUT)
125       status|= MYSQL_WAIT_WRITE;
126     if (pfd.revents & POLLPRI)
127       status|= MYSQL_WAIT_EXCEPT;
128     return status;
129   }
130 #endif
131 }
132 
async1(MYSQL * unused)133 static int async1(MYSQL *unused __attribute__((unused)))
134 {
135   int err= 0, rc;
136   MYSQL mysql, *ret;
137   MYSQL_RES *res;
138   MYSQL_ROW row;
139   int status;
140   uint default_timeout;
141   int i;
142 
143   if (skip_async)
144     return SKIP;
145 
146   for (i=0; i < 100; i++)
147   {
148 
149     mysql_init(&mysql);
150     rc= mysql_options(&mysql, MYSQL_OPT_NONBLOCK, 0);
151     check_mysql_rc(rc, (MYSQL *)&mysql);
152 
153     /* set timeouts to 300 microseconds */
154     default_timeout= 3;
155     mysql_options(&mysql, MYSQL_OPT_READ_TIMEOUT, &default_timeout);
156     mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &default_timeout);
157     mysql_options(&mysql, MYSQL_OPT_WRITE_TIMEOUT, &default_timeout);
158     mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "myapp");
159     if (force_tls)
160       mysql_ssl_set(&mysql, NULL, NULL, NULL, NULL,NULL);
161 
162     /* Returns 0 when done, else flag for what to wait for when need to block. */
163     status= mysql_real_connect_start(&ret, &mysql, hostname, username, password, schema, port, socketname, 0);
164     while (status)
165     {
166       status= wait_for_mysql(&mysql, status);
167       status= mysql_real_connect_cont(&ret, &mysql, status);
168     }
169     if (!ret)
170     {
171       diag("Error: %s", mysql_error(&mysql));
172       FAIL_IF(!ret, "Failed to mysql_real_connect()");
173     }
174 
175     if (force_tls && !mysql_get_ssl_cipher(&mysql))
176     {
177       diag("Error: No tls connection");
178       return FAIL;
179     }
180 
181     status= mysql_real_query_start(&err, &mysql, SL("SHOW STATUS"));
182     while (status)
183     {
184       status= wait_for_mysql(&mysql, status);
185       status= mysql_real_query_cont(&err, &mysql, status);
186     }
187     FAIL_IF(err, "mysql_real_query() returns error");
188 
189     /* This method cannot block. */
190     res= mysql_use_result(&mysql);
191     FAIL_IF(!res, "mysql_use_result() returns error");
192 
193     for (;;)
194     {
195       status= mysql_fetch_row_start(&row, res);
196       while (status)
197       {
198         status= wait_for_mysql(&mysql, status);
199         status= mysql_fetch_row_cont(&row, res, status);
200       }
201       if (!row)
202         break;
203     }
204     FAIL_IF(mysql_errno(&mysql), "Got error while retrieving rows");
205     mysql_free_result(res);
206 
207     /*
208       mysql_close() sends a COM_QUIT packet, and so in principle could block
209       waiting for the socket to accept the data.
210       In practise, for many applications it will probably be fine to use the
211       blocking mysql_close().
212      */
213     status= mysql_close_start(&mysql);
214     while (status)
215     {
216       status= wait_for_mysql(&mysql, status);
217       status= mysql_close_cont(&mysql, status);
218     }
219   }
220   return OK;
221 }
222 
test_conc131(MYSQL * unused)223 static int test_conc131(MYSQL *unused __attribute__((unused)))
224 {
225   int rc;
226   /* this test needs to run under valgrind */
227   MYSQL *mysql;
228 
229   if (skip_async)
230     return SKIP;
231 
232   mysql= mysql_init(NULL);
233   rc= mysql_options(mysql, MYSQL_OPT_NONBLOCK, 0);
234   check_mysql_rc(rc, mysql);
235   mysql_close(mysql);
236   return OK;
237 }
238 
test_conc129(MYSQL * unused)239 static int test_conc129(MYSQL *unused __attribute__((unused)))
240 {
241   MYSQL *mysql;
242 
243   if (skip_async)
244     return SKIP;
245 
246   mysql= mysql_init(NULL);
247   FAIL_IF(mysql_close_start(mysql), "No error expected");
248   return OK;
249 }
250 
251 
252 struct my_tests_st my_tests[] = {
253   {"test_async", test_async, TEST_CONNECTION_DEFAULT, 0,  NULL,  NULL},
254   {"async1", async1, TEST_CONNECTION_DEFAULT, 0,  NULL,  NULL},
255   {"test_conc131", test_conc131, TEST_CONNECTION_NONE, 0,  NULL,  NULL},
256   {"test_conc129", test_conc129, TEST_CONNECTION_NONE, 0,  NULL,  NULL},
257   {NULL, NULL, 0, 0, NULL, NULL}
258 };
259 
260 
main(int argc,char ** argv)261 int main(int argc, char **argv)
262 {
263   if (argc > 1)
264     get_options(argc, argv);
265 
266   get_envvars();
267 
268   run_tests(my_tests);
269 
270   return(exit_status());
271 }
272