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
31my_bool skip_async= 0;
32
33static 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("Asnyc IO not supported");
41 }
42 return OK;
43}
44
45static int
46wait_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
133static 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
223static 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
239static 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
252struct 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
261int 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