1
by Otto Kekäläinen
Import upstream version 5.5.32 |
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 |
/*
|
|
20 |
Run a set of queries in parallel against a server using the non-blocking
|
|
21 |
API, and compare to running same queries with the normal blocking API.
|
|
22 |
*/
|
|
23 |
||
24 |
#include <sys/time.h> |
|
25 |
#include <stdlib.h> |
|
26 |
#include <stdio.h> |
|
27 |
#include <string.h> |
|
28 |
||
29 |
#include <my_global.h> |
|
30 |
#include <my_sys.h> |
|
31 |
#include <mysql.h> |
|
32 |
#include <my_getopt.h> |
|
33 |
||
34 |
#include <event.h> |
|
35 |
||
36 |
||
37 |
#define SL(s) (s), sizeof(s)
|
|
38 |
static const char *my_groups[]= { "client", NULL }; |
|
39 |
||
40 |
/* Maintaining a list of queries to run. */
|
|
41 |
struct query_entry { |
|
42 |
struct query_entry *next; |
|
43 |
char *query; |
|
44 |
int index; |
|
45 |
};
|
|
46 |
static struct query_entry *query_list; |
|
47 |
static struct query_entry **tail_ptr= &query_list; |
|
48 |
static int query_counter= 0; |
|
49 |
||
50 |
||
51 |
/* State kept for each connection. */
|
|
52 |
struct state_data { |
|
53 |
int ST; /* State machine current state */ |
|
54 |
struct event ev_mysql; |
|
55 |
MYSQL mysql; |
|
56 |
MYSQL_RES *result; |
|
57 |
MYSQL *ret; |
|
58 |
int err; |
|
59 |
MYSQL_ROW row; |
|
60 |
struct query_entry *query_element; |
|
61 |
int index; |
|
62 |
};
|
|
63 |
||
64 |
||
65 |
static const char *opt_db= NULL; |
|
66 |
static const char *opt_user= NULL; |
|
67 |
static const char *opt_password= NULL; |
|
68 |
static int tty_password= 0; |
|
69 |
static const char *opt_host= NULL; |
|
70 |
static const char *opt_socket= NULL; |
|
71 |
static unsigned int opt_port= 0; |
|
72 |
static unsigned int opt_connections= 5; |
|
73 |
static const char *opt_query_file= NULL; |
|
74 |
||
75 |
static struct my_option options[] = |
|
76 |
{
|
|
77 |
{"database", 'D', "Database to use", &opt_db, &opt_db, |
|
78 |
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
79 |
{"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, |
|
80 |
0, 0, 0, 0, 0}, |
|
81 |
{"host", 'h', "Connect to host", &opt_host, &opt_host, |
|
82 |
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
83 |
{"password", 'p', |
|
84 |
"Password to use when connecting to server. If password is not given it's asked from the tty.", |
|
85 |
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
|
86 |
{"port", 'P', "Port number to use for connection.", |
|
87 |
&opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
88 |
{"socket", 'S', "Socket file to use for connection", |
|
89 |
&opt_socket, &opt_socket, 0, GET_STR, |
|
90 |
REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
91 |
{"user", 'u', "User for login if not current user", &opt_user, |
|
92 |
&opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
|
93 |
{"connections", 'n', "Number of simultaneous connections/queries.", |
|
94 |
&opt_connections, &opt_connections, 0, GET_UINT, REQUIRED_ARG, |
|
95 |
5, 0, 0, 0, 0, 0}, |
|
96 |
{"queryfile", 'q', "Name of file containing extra queries to run", |
|
97 |
&opt_query_file, &opt_query_file, 0, GET_STR, REQUIRED_ARG, |
|
98 |
0, 0, 0, 0, 0, 0}, |
|
99 |
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
|
100 |
};
|
|
101 |
||
102 |
static void |
|
103 |
fatal(struct state_data *sd, const char *msg) |
|
104 |
{
|
|
105 |
fprintf(stderr, "%s: %s\n", msg, (sd ? mysql_error(&sd->mysql) : "")); |
|
106 |
exit(1); |
|
107 |
}
|
|
108 |
||
109 |
||
110 |
static void state_machine_handler(int fd, short event, void *arg); |
|
111 |
||
112 |
static void |
|
113 |
next_event(int new_st, int status, struct state_data *sd) |
|
114 |
{
|
|
115 |
short wait_event= 0; |
|
116 |
struct timeval tv, *ptv; |
|
117 |
int fd; |
|
118 |
||
119 |
if (status & MYSQL_WAIT_READ) |
|
120 |
wait_event|= EV_READ; |
|
121 |
if (status & MYSQL_WAIT_WRITE) |
|
122 |
wait_event|= EV_WRITE; |
|
123 |
if (wait_event) |
|
124 |
fd= mysql_get_socket(&sd->mysql); |
|
125 |
else
|
|
126 |
fd= -1; |
|
127 |
if (status & MYSQL_WAIT_TIMEOUT) |
|
128 |
{
|
|
129 |
tv.tv_sec= mysql_get_timeout_value(&sd->mysql); |
|
130 |
tv.tv_usec= 0; |
|
131 |
ptv= &tv; |
|
132 |
}
|
|
133 |
else
|
|
134 |
ptv= NULL; |
|
135 |
event_set(&sd->ev_mysql, fd, wait_event, state_machine_handler, sd); |
|
136 |
event_add(&sd->ev_mysql, ptv); |
|
137 |
sd->ST= new_st; |
|
138 |
}
|
|
139 |
||
140 |
static int |
|
141 |
mysql_status(short event) |
|
142 |
{
|
|
143 |
int status= 0; |
|
144 |
if (event & EV_READ) |
|
145 |
status|= MYSQL_WAIT_READ; |
|
146 |
if (event & EV_WRITE) |
|
147 |
status|= MYSQL_WAIT_WRITE; |
|
148 |
if (event & EV_TIMEOUT) |
|
149 |
status|= MYSQL_WAIT_TIMEOUT; |
|
150 |
return status; |
|
151 |
}
|
|
152 |
||
153 |
||
154 |
static int num_active_connections; |
|
155 |
||
156 |
/* Shortcut for going to new state immediately without waiting. */
|
|
157 |
#define NEXT_IMMEDIATE(sd_, new_st) do { sd_->ST= new_st; goto again; } while (0)
|
|
158 |
||
159 |
static void |
|
160 |
state_machine_handler(int fd __attribute__((unused)), short event, void *arg) |
|
161 |
{
|
|
162 |
struct state_data *sd= arg; |
|
163 |
int status; |
|
164 |
||
165 |
again: |
|
166 |
switch(sd->ST) |
|
167 |
{
|
|
168 |
case 0: |
|
169 |
/* Initial state, start making the connection. */
|
|
170 |
status= mysql_real_connect_start(&sd->ret, &sd->mysql, opt_host, opt_user, opt_password, opt_db, opt_port, opt_socket, 0); |
|
171 |
if (status) |
|
172 |
/* Wait for connect to complete. */
|
|
173 |
next_event(1, status, sd); |
|
174 |
else
|
|
175 |
NEXT_IMMEDIATE(sd, 9); |
|
176 |
break; |
|
177 |
||
178 |
case 1: |
|
179 |
status= mysql_real_connect_cont(&sd->ret, &sd->mysql, mysql_status(event)); |
|
180 |
if (status) |
|
181 |
next_event(1, status, sd); |
|
182 |
else
|
|
183 |
NEXT_IMMEDIATE(sd, 9); |
|
184 |
break; |
|
185 |
||
186 |
case 9: |
|
187 |
if (!sd->ret) |
|
188 |
fatal(sd, "Failed to mysql_real_connect()"); |
|
189 |
NEXT_IMMEDIATE(sd, 10); |
|
190 |
break; |
|
191 |
||
192 |
case 10: |
|
193 |
/* Now run the next query. */
|
|
194 |
sd->query_element= query_list; |
|
195 |
if (!sd->query_element) |
|
196 |
{
|
|
197 |
/* No more queries, end the connection. */
|
|
198 |
NEXT_IMMEDIATE(sd, 40); |
|
199 |
}
|
|
200 |
query_list= query_list->next; |
|
201 |
||
202 |
sd->index= sd->query_element->index; |
|
203 |
printf("%d ! %s\n", sd->index, sd->query_element->query); |
|
204 |
status= mysql_real_query_start(&sd->err, &sd->mysql, sd->query_element->query, |
|
205 |
strlen(sd->query_element->query)); |
|
206 |
if (status) |
|
207 |
next_event(11, status, sd); |
|
208 |
else
|
|
209 |
NEXT_IMMEDIATE(sd, 20); |
|
210 |
break; |
|
211 |
||
212 |
case 11: |
|
213 |
status= mysql_real_query_cont(&sd->err, &sd->mysql, mysql_status(event)); |
|
214 |
if (status) |
|
215 |
next_event(11, status, sd); |
|
216 |
else
|
|
217 |
NEXT_IMMEDIATE(sd, 20); |
|
218 |
break; |
|
219 |
||
220 |
case 20: |
|
221 |
my_free(sd->query_element->query); |
|
222 |
my_free(sd->query_element); |
|
223 |
if (sd->err) |
|
224 |
{
|
|
225 |
printf("%d | Error: %s\n", sd->index, mysql_error(&sd->mysql)); |
|
226 |
NEXT_IMMEDIATE(sd, 10); |
|
227 |
}
|
|
228 |
else
|
|
229 |
{
|
|
230 |
sd->result= mysql_use_result(&sd->mysql); |
|
231 |
if (!sd->result) |
|
232 |
fatal(sd, "mysql_use_result() returns error"); |
|
233 |
NEXT_IMMEDIATE(sd, 30); |
|
234 |
}
|
|
235 |
break; |
|
236 |
||
237 |
case 30: |
|
238 |
status= mysql_fetch_row_start(&sd->row, sd->result); |
|
239 |
if (status) |
|
240 |
next_event(31, status, sd); |
|
241 |
else
|
|
242 |
NEXT_IMMEDIATE(sd, 39); |
|
243 |
break; |
|
244 |
||
245 |
case 31: |
|
246 |
status= mysql_fetch_row_cont(&sd->row, sd->result, mysql_status(event)); |
|
247 |
if (status) |
|
248 |
next_event(31, status, sd); |
|
249 |
else
|
|
250 |
NEXT_IMMEDIATE(sd, 39); |
|
251 |
break; |
|
252 |
||
253 |
case 39: |
|
254 |
if (sd->row) |
|
255 |
{
|
|
256 |
/* Got a row. */
|
|
257 |
unsigned int i; |
|
258 |
printf("%d - ", sd->index); |
|
259 |
for (i= 0; i < mysql_num_fields(sd->result); i++) |
|
260 |
printf("%s%s", (i ? "\t" : ""), (sd->row[i] ? sd->row[i] : "(null)")); |
|
261 |
printf ("\n"); |
|
262 |
NEXT_IMMEDIATE(sd, 30); |
|
263 |
}
|
|
264 |
else
|
|
265 |
{
|
|
266 |
if (mysql_errno(&sd->mysql)) |
|
267 |
{
|
|
268 |
/* An error occured. */
|
|
269 |
printf("%d | Error: %s\n", sd->index, mysql_error(&sd->mysql)); |
|
270 |
}
|
|
271 |
else
|
|
272 |
{
|
|
273 |
/* EOF. */
|
|
274 |
printf("%d | EOF\n", sd->index); |
|
275 |
}
|
|
276 |
mysql_free_result(sd->result); |
|
277 |
NEXT_IMMEDIATE(sd, 10); |
|
278 |
}
|
|
279 |
break; |
|
280 |
||
281 |
case 40: |
|
282 |
status= mysql_close_start(&sd->mysql); |
|
283 |
if (status) |
|
284 |
next_event(41, status, sd); |
|
285 |
else
|
|
286 |
NEXT_IMMEDIATE(sd, 50); |
|
287 |
break; |
|
288 |
||
289 |
case 41: |
|
290 |
status= mysql_close_cont(&sd->mysql, mysql_status(event)); |
|
291 |
if (status) |
|
292 |
next_event(41, status, sd); |
|
293 |
else
|
|
294 |
NEXT_IMMEDIATE(sd, 50); |
|
295 |
break; |
|
296 |
||
297 |
case 50: |
|
298 |
/* We are done! */
|
|
299 |
num_active_connections--; |
|
300 |
if (num_active_connections == 0) |
|
301 |
event_loopbreak(); |
|
302 |
break; |
|
303 |
||
304 |
default: |
|
305 |
abort(); |
|
306 |
}
|
|
307 |
}
|
|
308 |
||
309 |
||
310 |
void
|
|
311 |
add_query(const char *q) |
|
312 |
{
|
|
313 |
struct query_entry *e; |
|
314 |
char *q2; |
|
315 |
size_t len; |
|
316 |
||
317 |
e= my_malloc(sizeof(*e), MYF(0)); |
|
318 |
q2= my_strdup(q, MYF(0)); |
|
319 |
if (!e || !q2) |
|
320 |
fatal(NULL, "Out of memory"); |
|
321 |
||
322 |
/* Remove any trailing newline. */
|
|
323 |
len= strlen(q2); |
|
324 |
if (q2[len] == '\n') |
|
325 |
q2[len--]= '\0'; |
|
326 |
if (q2[len] == '\r') |
|
327 |
q2[len--]= '\0'; |
|
328 |
||
329 |
e->next= NULL; |
|
330 |
e->query= q2; |
|
331 |
e->index= query_counter++; |
|
332 |
*tail_ptr= e; |
|
333 |
tail_ptr= &e->next; |
|
334 |
}
|
|
335 |
||
336 |
||
337 |
static my_bool |
|
338 |
handle_option(int optid, const struct my_option *opt __attribute__((unused)), |
|
339 |
char *arg) |
|
340 |
{
|
|
341 |
switch (optid) |
|
342 |
{
|
|
343 |
case '?': |
|
344 |
printf("Usage: async_queries [OPTIONS] query ...\n"); |
|
345 |
my_print_help(options); |
|
346 |
my_print_variables(options); |
|
347 |
exit(0); |
|
348 |
break; |
|
349 |
||
350 |
case 'p': |
|
351 |
if (arg) |
|
352 |
opt_password= arg; |
|
353 |
else
|
|
354 |
tty_password= 1; |
|
355 |
break; |
|
356 |
}
|
|
357 |
||
358 |
return 0; |
|
359 |
}
|
|
360 |
||
361 |
||
362 |
int
|
|
363 |
main(int argc, char *argv[]) |
|
364 |
{
|
|
365 |
struct state_data *sds; |
|
366 |
unsigned int i; |
|
367 |
int err; |
|
368 |
struct event_base *libevent_base; |
|
369 |
||
370 |
err= handle_options(&argc, &argv, options, handle_option); |
|
371 |
if (err) |
|
372 |
exit(err); |
|
373 |
if (tty_password) |
|
374 |
opt_password= get_tty_password(NullS); |
|
375 |
||
376 |
if (opt_query_file) |
|
377 |
{
|
|
378 |
FILE *f= fopen(opt_query_file, "r"); |
|
379 |
char buf[65536]; |
|
380 |
if (!f) |
|
381 |
fatal(NULL, "Cannot open query file"); |
|
382 |
while (!feof(f)) |
|
383 |
{
|
|
384 |
if (!fgets(buf, sizeof(buf), f)) |
|
385 |
break; |
|
386 |
add_query(buf); |
|
387 |
}
|
|
388 |
fclose(f); |
|
389 |
}
|
|
390 |
/* Add extra queries directly on command line. */
|
|
391 |
while (argc > 0) |
|
392 |
{
|
|
393 |
--argc; |
|
394 |
add_query(*argv++); |
|
395 |
}
|
|
396 |
||
397 |
sds= my_malloc(opt_connections * sizeof(*sds), MYF(0)); |
|
398 |
if (!sds) |
|
399 |
fatal(NULL, "Out of memory"); |
|
400 |
||
401 |
libevent_base= event_init(); |
|
402 |
||
403 |
err= mysql_library_init(argc, argv, (char **)my_groups); |
|
404 |
if (err) |
|
405 |
{
|
|
406 |
fprintf(stderr, "Fatal: mysql_library_init() returns error: %d\n", err); |
|
407 |
exit(1); |
|
408 |
}
|
|
409 |
||
410 |
num_active_connections= 0; |
|
411 |
for (i= 0; i < opt_connections; i++) |
|
412 |
{
|
|
413 |
mysql_init(&sds[i].mysql); |
|
414 |
mysql_options(&sds[i].mysql, MYSQL_OPT_NONBLOCK, 0); |
|
415 |
mysql_options(&sds[i].mysql, MYSQL_READ_DEFAULT_GROUP, "async_queries"); |
|
416 |
||
417 |
/*
|
|
418 |
We put the initial connect call in the first state 0 of the state machine
|
|
419 |
and run that manually, just to have everything in one place.
|
|
420 |
*/
|
|
421 |
sds[i].ST= 0; |
|
422 |
num_active_connections++; |
|
423 |
state_machine_handler(-1, -1, &sds[i]); |
|
424 |
}
|
|
425 |
||
426 |
event_dispatch(); |
|
427 |
||
428 |
free(sds); |
|
429 |
||
430 |
mysql_library_end(); |
|
431 |
||
432 |
event_base_free(libevent_base); |
|
433 |
||
434 |
return 0; |
|
435 |
}
|