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 | |
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("Asnyc IO not supported" ); |
41 | } |
42 | return OK; |
43 | } |
44 | |
45 | static int |
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 | |
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 | |
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 | |
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 | |
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 | |