mysql2 0.3.18-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+
5
+ require 'rubygems'
6
+ require 'eventmachine'
7
+ require 'mysql2/em'
8
+
9
+ EM.run do
10
+ client1 = Mysql2::EM::Client.new
11
+ defer1 = client1.query "SELECT sleep(3) as first_query"
12
+ defer1.callback do |result|
13
+ puts "Result: #{result.to_a.inspect}"
14
+ end
15
+
16
+ client2 = Mysql2::EM::Client.new
17
+ defer2 = client2.query "SELECT sleep(1) second_query"
18
+ defer2.callback do |result|
19
+ puts "Result: #{result.to_a.inspect}"
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ $LOAD_PATH.unshift 'lib'
4
+ require 'mysql2'
5
+ require 'timeout'
6
+
7
+ threads = []
8
+ # Should never exceed worst case 3.5 secs across all 20 threads
9
+ Timeout.timeout(3.5) do
10
+ 20.times do
11
+ threads << Thread.new do
12
+ overhead = rand(3)
13
+ puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
14
+ # 3 second overhead per query
15
+ Mysql2::Client.new(:host => "localhost", :username => "root").query("SELECT sleep(#{overhead}) as result")
16
+ puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
17
+ end
18
+ end
19
+ threads.each{|t| t.join }
20
+ end
@@ -0,0 +1,1416 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ #include <time.h>
4
+ #include <errno.h>
5
+ #ifndef _WIN32
6
+ #include <sys/types.h>
7
+ #include <sys/socket.h>
8
+ #endif
9
+ #ifndef _MSC_VER
10
+ #include <unistd.h>
11
+ #endif
12
+ #include <fcntl.h>
13
+ #include "wait_for_single_fd.h"
14
+
15
+ #include "mysql_enc_name_to_ruby.h"
16
+
17
+ VALUE cMysql2Client;
18
+ extern VALUE mMysql2, cMysql2Error;
19
+ static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
20
+ static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
21
+
22
+ #ifndef HAVE_RB_HASH_DUP
23
+ static VALUE rb_hash_dup(VALUE other) {
24
+ return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
25
+ }
26
+ #endif
27
+
28
+ #define REQUIRE_INITIALIZED(wrapper) \
29
+ if (!wrapper->initialized) { \
30
+ rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
+ }
32
+
33
+ #define REQUIRE_CONNECTED(wrapper) \
34
+ REQUIRE_INITIALIZED(wrapper) \
35
+ if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36
+ rb_raise(cMysql2Error, "closed MySQL connection"); \
37
+ }
38
+
39
+ #define REQUIRE_NOT_CONNECTED(wrapper) \
40
+ REQUIRE_INITIALIZED(wrapper) \
41
+ if (wrapper->connected) { \
42
+ rb_raise(cMysql2Error, "MySQL connection is already open"); \
43
+ }
44
+
45
+ #define MARK_CONN_INACTIVE(conn) \
46
+ wrapper->active_thread = Qnil;
47
+
48
+ #define GET_CLIENT(self) \
49
+ mysql_client_wrapper *wrapper; \
50
+ Data_Get_Struct(self, mysql_client_wrapper, wrapper)
51
+
52
+ /*
53
+ * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
54
+ * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
55
+ * linking against the server itself
56
+ */
57
+ #ifdef LIBMYSQL_VERSION
58
+ #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
59
+ #else
60
+ #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
61
+ #endif
62
+
63
+ /*
64
+ * used to pass all arguments to mysql_real_connect while inside
65
+ * rb_thread_call_without_gvl
66
+ */
67
+ struct nogvl_connect_args {
68
+ MYSQL *mysql;
69
+ const char *host;
70
+ const char *user;
71
+ const char *passwd;
72
+ const char *db;
73
+ unsigned int port;
74
+ const char *unix_socket;
75
+ unsigned long client_flag;
76
+ };
77
+
78
+ /*
79
+ * used to pass all arguments to mysql_send_query while inside
80
+ * rb_thread_call_without_gvl
81
+ */
82
+ struct nogvl_send_query_args {
83
+ MYSQL *mysql;
84
+ VALUE sql;
85
+ const char *sql_ptr;
86
+ long sql_len;
87
+ mysql_client_wrapper *wrapper;
88
+ };
89
+
90
+ /*
91
+ * used to pass all arguments to mysql_select_db while inside
92
+ * rb_thread_call_without_gvl
93
+ */
94
+ struct nogvl_select_db_args {
95
+ MYSQL *mysql;
96
+ char *db;
97
+ };
98
+
99
+ /*
100
+ * non-blocking mysql_*() functions that we won't be wrapping since
101
+ * they do not appear to hit the network nor issue any interruptible
102
+ * or blocking system calls.
103
+ *
104
+ * - mysql_affected_rows()
105
+ * - mysql_error()
106
+ * - mysql_fetch_fields()
107
+ * - mysql_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
108
+ * - mysql_field_count()
109
+ * - mysql_get_client_info()
110
+ * - mysql_get_client_version()
111
+ * - mysql_get_server_info()
112
+ * - mysql_get_server_version()
113
+ * - mysql_insert_id()
114
+ * - mysql_num_fields()
115
+ * - mysql_num_rows()
116
+ * - mysql_options()
117
+ * - mysql_real_escape_string()
118
+ * - mysql_ssl_set()
119
+ */
120
+
121
+ static void rb_mysql_client_mark(void * wrapper) {
122
+ mysql_client_wrapper * w = wrapper;
123
+ if (w) {
124
+ rb_gc_mark(w->encoding);
125
+ rb_gc_mark(w->active_thread);
126
+ }
127
+ }
128
+
129
+ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
130
+ VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
131
+ VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
132
+ VALUE e;
133
+
134
+ #ifdef HAVE_RUBY_ENCODING_H
135
+ rb_enc_associate(rb_error_msg, rb_utf8_encoding());
136
+ rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137
+ #endif
138
+
139
+ e = rb_funcall(cMysql2Error, rb_intern("new"), 2, rb_error_msg, LONG2FIX(wrapper->server_version));
140
+ rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
141
+ rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
142
+ rb_exc_raise(e);
143
+ return Qnil;
144
+ }
145
+
146
+ static void *nogvl_init(void *ptr) {
147
+ MYSQL *client;
148
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
149
+
150
+ /* may initialize embedded server and read /etc/services off disk */
151
+ client = mysql_init(wrapper->client);
152
+
153
+ if (client) mysql2_set_local_infile(client, wrapper);
154
+
155
+ return (void*)(client ? Qtrue : Qfalse);
156
+ }
157
+
158
+ static void *nogvl_connect(void *ptr) {
159
+ struct nogvl_connect_args *args = ptr;
160
+ MYSQL *client;
161
+
162
+ client = mysql_real_connect(args->mysql, args->host,
163
+ args->user, args->passwd,
164
+ args->db, args->port, args->unix_socket,
165
+ args->client_flag);
166
+
167
+ return (void *)(client ? Qtrue : Qfalse);
168
+ }
169
+
170
+ #ifndef _WIN32
171
+ /*
172
+ * Redirect clientfd to /dev/null for mysql_close and SSL_close to write,
173
+ * shutdown, and close. The hack is needed to prevent shutdown() from breaking
174
+ * a socket that may be in use by the parent or other processes after fork.
175
+ *
176
+ * /dev/null is used to absorb writes; previously a dummy socket was used, but
177
+ * it could not abosrb writes and caused openssl to go into an infinite loop.
178
+ *
179
+ * Returns Qtrue or Qfalse (success or failure)
180
+ *
181
+ * Note: if this function is needed on Windows, use "nul" instead of "/dev/null"
182
+ */
183
+ static VALUE invalidate_fd(int clientfd)
184
+ {
185
+ #ifdef SOCK_CLOEXEC
186
+ /* Atomically set CLOEXEC on the new FD in case another thread forks */
187
+ int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
188
+ if (sockfd < 0) {
189
+ /* Maybe SOCK_CLOEXEC is defined but not available on this kernel */
190
+ int sockfd = open("/dev/null", O_RDWR);
191
+ fcntl(sockfd, F_SETFD, FD_CLOEXEC);
192
+ }
193
+ #else
194
+ /* Well we don't have SOCK_CLOEXEC, so just set FD_CLOEXEC quickly */
195
+ int sockfd = open("/dev/null", O_RDWR);
196
+ fcntl(sockfd, F_SETFD, FD_CLOEXEC);
197
+ #endif
198
+
199
+ if (sockfd < 0) {
200
+ /*
201
+ * Cannot raise here, because one or both of the following may be true:
202
+ * a) we have no GVL (in C Ruby)
203
+ * b) are running as a GC finalizer
204
+ */
205
+ return Qfalse;
206
+ }
207
+
208
+ dup2(sockfd, clientfd);
209
+ close(sockfd);
210
+
211
+ return Qtrue;
212
+ }
213
+ #endif /* _WIN32 */
214
+
215
+ static void *nogvl_close(void *ptr) {
216
+ mysql_client_wrapper *wrapper;
217
+ wrapper = ptr;
218
+ if (wrapper->connected) {
219
+ wrapper->active_thread = Qnil;
220
+ wrapper->connected = 0;
221
+ #ifndef _WIN32
222
+ /* Invalidate the socket before calling mysql_close(). This prevents
223
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
224
+ * the socket. The difference is that invalidate_fd will drop this
225
+ * process's reference to the socket only, while a QUIT or shutdown()
226
+ * would render the underlying connection unusable, interrupting other
227
+ * processes which share this object across a fork().
228
+ */
229
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
230
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
231
+ close(wrapper->client->net.fd);
232
+ return NULL;
233
+ }
234
+ #endif
235
+
236
+ mysql_close(wrapper->client); /* only used to free memory at this point */
237
+ }
238
+
239
+ return NULL;
240
+ }
241
+
242
+ static void rb_mysql_client_free(void *ptr) {
243
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
244
+ decr_mysql2_client(wrapper);
245
+ }
246
+
247
+ void decr_mysql2_client(mysql_client_wrapper *wrapper)
248
+ {
249
+ wrapper->refcount--;
250
+ if (wrapper->refcount == 0) {
251
+ nogvl_close(wrapper);
252
+ xfree(wrapper->client);
253
+ xfree(wrapper);
254
+ }
255
+ }
256
+
257
+ static VALUE allocate(VALUE klass) {
258
+ VALUE obj;
259
+ mysql_client_wrapper * wrapper;
260
+ obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
+ wrapper->encoding = Qnil;
262
+ wrapper->active_thread = Qnil;
263
+ wrapper->server_version = 0;
264
+ wrapper->reconnect_enabled = 0;
265
+ wrapper->connect_timeout = 0;
266
+ wrapper->connected = 0; /* means that a database connection is open */
267
+ wrapper->initialized = 0; /* means that that the wrapper is initialized */
268
+ wrapper->refcount = 1;
269
+ wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
270
+ return obj;
271
+ }
272
+
273
+ /* call-seq:
274
+ * Mysql2::Client.escape(string)
275
+ *
276
+ * Escape +string+ so that it may be used in a SQL statement.
277
+ * Note that this escape method is not connection encoding aware.
278
+ * If you need encoding support use Mysql2::Client#escape instead.
279
+ */
280
+ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
281
+ unsigned char *newStr;
282
+ VALUE rb_str;
283
+ unsigned long newLen, oldLen;
284
+
285
+ Check_Type(str, T_STRING);
286
+
287
+ oldLen = RSTRING_LEN(str);
288
+ newStr = xmalloc(oldLen*2+1);
289
+
290
+ newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
291
+ if (newLen == oldLen) {
292
+ /* no need to return a new ruby string if nothing changed */
293
+ xfree(newStr);
294
+ return str;
295
+ } else {
296
+ rb_str = rb_str_new((const char*)newStr, newLen);
297
+ #ifdef HAVE_RUBY_ENCODING_H
298
+ rb_enc_copy(rb_str, str);
299
+ #endif
300
+ xfree(newStr);
301
+ return rb_str;
302
+ }
303
+ }
304
+
305
+ static VALUE rb_mysql_client_warning_count(VALUE self) {
306
+ unsigned int warning_count;
307
+ GET_CLIENT(self);
308
+
309
+ warning_count = mysql_warning_count(wrapper->client);
310
+
311
+ return UINT2NUM(warning_count);
312
+ }
313
+
314
+ static VALUE rb_mysql_info(VALUE self) {
315
+ const char *info;
316
+ VALUE rb_str;
317
+ GET_CLIENT(self);
318
+
319
+ info = mysql_info(wrapper->client);
320
+
321
+ if (info == NULL) {
322
+ return Qnil;
323
+ }
324
+
325
+ rb_str = rb_str_new2(info);
326
+ #ifdef HAVE_RUBY_ENCODING_H
327
+ rb_enc_associate(rb_str, rb_utf8_encoding());
328
+ #endif
329
+
330
+ return rb_str;
331
+ }
332
+
333
+ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
334
+ struct nogvl_connect_args args;
335
+ time_t start_time, end_time;
336
+ unsigned int elapsed_time, connect_timeout;
337
+ VALUE rv;
338
+ GET_CLIENT(self);
339
+
340
+ args.host = NIL_P(host) ? NULL : StringValuePtr(host);
341
+ args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
342
+ args.port = NIL_P(port) ? 0 : NUM2INT(port);
343
+ args.user = NIL_P(user) ? NULL : StringValuePtr(user);
344
+ args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
345
+ args.db = NIL_P(database) ? NULL : StringValuePtr(database);
346
+ args.mysql = wrapper->client;
347
+ args.client_flag = NUM2ULONG(flags);
348
+
349
+ if (wrapper->connect_timeout)
350
+ time(&start_time);
351
+ rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
352
+ if (rv == Qfalse) {
353
+ while (rv == Qfalse && errno == EINTR) {
354
+ if (wrapper->connect_timeout) {
355
+ time(&end_time);
356
+ /* avoid long connect timeout from system time changes */
357
+ if (end_time < start_time)
358
+ start_time = end_time;
359
+ elapsed_time = end_time - start_time;
360
+ /* avoid an early timeout due to time truncating milliseconds off the start time */
361
+ if (elapsed_time > 0)
362
+ elapsed_time--;
363
+ if (elapsed_time >= wrapper->connect_timeout)
364
+ break;
365
+ connect_timeout = wrapper->connect_timeout - elapsed_time;
366
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
367
+ }
368
+ errno = 0;
369
+ rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
370
+ }
371
+ /* restore the connect timeout for reconnecting */
372
+ if (wrapper->connect_timeout)
373
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
+ if (rv == Qfalse)
375
+ return rb_raise_mysql2_error(wrapper);
376
+ }
377
+
378
+ wrapper->server_version = mysql_get_server_version(wrapper->client);
379
+ wrapper->connected = 1;
380
+ return self;
381
+ }
382
+
383
+ /*
384
+ * Immediately disconnect from the server, normally the garbage collector
385
+ * will disconnect automatically when a connection is no longer needed.
386
+ * Explicitly closing this will free up server resources sooner than waiting
387
+ * for the garbage collector.
388
+ */
389
+ static VALUE rb_mysql_client_close(VALUE self) {
390
+ GET_CLIENT(self);
391
+
392
+ if (wrapper->connected) {
393
+ rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
394
+ }
395
+
396
+ return Qnil;
397
+ }
398
+
399
+ /*
400
+ * mysql_send_query is unlikely to block since most queries are small
401
+ * enough to fit in a socket buffer, but sometimes large UPDATE and
402
+ * INSERTs will cause the process to block
403
+ */
404
+ static void *nogvl_send_query(void *ptr) {
405
+ struct nogvl_send_query_args *args = ptr;
406
+ int rv;
407
+
408
+ rv = mysql_send_query(args->mysql, args->sql_ptr, args->sql_len);
409
+
410
+ return (void*)(rv == 0 ? Qtrue : Qfalse);
411
+ }
412
+
413
+ static VALUE do_send_query(void *args) {
414
+ struct nogvl_send_query_args *query_args = args;
415
+ mysql_client_wrapper *wrapper = query_args->wrapper;
416
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
417
+ /* an error occurred, we're not active anymore */
418
+ MARK_CONN_INACTIVE(self);
419
+ return rb_raise_mysql2_error(wrapper);
420
+ }
421
+ return Qnil;
422
+ }
423
+
424
+ /*
425
+ * even though we did rb_thread_select before calling this, a large
426
+ * response can overflow the socket buffers and cause us to eventually
427
+ * block while calling mysql_read_query_result
428
+ */
429
+ static void *nogvl_read_query_result(void *ptr) {
430
+ MYSQL * client = ptr;
431
+ my_bool res = mysql_read_query_result(client);
432
+
433
+ return (void *)(res == 0 ? Qtrue : Qfalse);
434
+ }
435
+
436
+ static void *nogvl_do_result(void *ptr, char use_result) {
437
+ mysql_client_wrapper *wrapper;
438
+ MYSQL_RES *result;
439
+
440
+ wrapper = (mysql_client_wrapper *)ptr;
441
+ if (use_result) {
442
+ result = mysql_use_result(wrapper->client);
443
+ } else {
444
+ result = mysql_store_result(wrapper->client);
445
+ }
446
+
447
+ /* once our result is stored off, this connection is
448
+ ready for another command to be issued */
449
+ wrapper->active_thread = Qnil;
450
+
451
+ return result;
452
+ }
453
+
454
+ /* mysql_store_result may (unlikely) read rows off the socket */
455
+ static void *nogvl_store_result(void *ptr) {
456
+ return nogvl_do_result(ptr, 0);
457
+ }
458
+
459
+ static void *nogvl_use_result(void *ptr) {
460
+ return nogvl_do_result(ptr, 1);
461
+ }
462
+
463
+ /* call-seq:
464
+ * client.async_result
465
+ *
466
+ * Returns the result for the last async issued query.
467
+ */
468
+ static VALUE rb_mysql_client_async_result(VALUE self) {
469
+ MYSQL_RES * result;
470
+ VALUE resultObj;
471
+ VALUE current, is_streaming;
472
+ GET_CLIENT(self);
473
+
474
+ /* if we're not waiting on a result, do nothing */
475
+ if (NIL_P(wrapper->active_thread))
476
+ return Qnil;
477
+
478
+ REQUIRE_CONNECTED(wrapper);
479
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
480
+ /* an error occurred, mark this connection inactive */
481
+ MARK_CONN_INACTIVE(self);
482
+ return rb_raise_mysql2_error(wrapper);
483
+ }
484
+
485
+ is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
486
+ if (is_streaming == Qtrue) {
487
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
488
+ } else {
489
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
490
+ }
491
+
492
+ if (result == NULL) {
493
+ if (mysql_errno(wrapper->client) != 0) {
494
+ MARK_CONN_INACTIVE(self);
495
+ rb_raise_mysql2_error(wrapper);
496
+ }
497
+ /* no data and no error, so query was not a SELECT */
498
+ return Qnil;
499
+ }
500
+
501
+ current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
502
+ RB_GC_GUARD(current);
503
+ Check_Type(current, T_HASH);
504
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
505
+
506
+ return resultObj;
507
+ }
508
+
509
+ #ifndef _WIN32
510
+ struct async_query_args {
511
+ int fd;
512
+ VALUE self;
513
+ };
514
+
515
+ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
516
+ GET_CLIENT(self);
517
+
518
+ wrapper->active_thread = Qnil;
519
+ wrapper->connected = 0;
520
+
521
+ /* Invalidate the MySQL socket to prevent further communication.
522
+ * The GC will come along later and call mysql_close to free it.
523
+ */
524
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
525
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
526
+ close(wrapper->client->net.fd);
527
+ }
528
+
529
+ rb_exc_raise(error);
530
+
531
+ return Qnil;
532
+ }
533
+
534
+ static VALUE do_query(void *args) {
535
+ struct async_query_args *async_args;
536
+ struct timeval tv;
537
+ struct timeval* tvp;
538
+ long int sec;
539
+ int retval;
540
+ VALUE read_timeout;
541
+
542
+ async_args = (struct async_query_args *)args;
543
+ read_timeout = rb_iv_get(async_args->self, "@read_timeout");
544
+
545
+ tvp = NULL;
546
+ if (!NIL_P(read_timeout)) {
547
+ Check_Type(read_timeout, T_FIXNUM);
548
+ tvp = &tv;
549
+ sec = FIX2INT(read_timeout);
550
+ /* TODO: support partial seconds?
551
+ also, this check is here for sanity, we also check up in Ruby */
552
+ if (sec >= 0) {
553
+ tvp->tv_sec = sec;
554
+ } else {
555
+ rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
556
+ }
557
+ tvp->tv_usec = 0;
558
+ }
559
+
560
+ for(;;) {
561
+ retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
562
+
563
+ if (retval == 0) {
564
+ rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
565
+ }
566
+
567
+ if (retval < 0) {
568
+ rb_sys_fail(0);
569
+ }
570
+
571
+ if (retval > 0) {
572
+ break;
573
+ }
574
+ }
575
+
576
+ return Qnil;
577
+ }
578
+ #else
579
+ static VALUE finish_and_mark_inactive(void *args) {
580
+ VALUE self;
581
+ MYSQL_RES *result;
582
+
583
+ self = (VALUE)args;
584
+
585
+ GET_CLIENT(self);
586
+
587
+ if (!NIL_P(wrapper->active_thread)) {
588
+ /* if we got here, the result hasn't been read off the wire yet
589
+ so lets do that and then throw it away because we have no way
590
+ of getting it back up to the caller from here */
591
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
592
+ mysql_free_result(result);
593
+
594
+ wrapper->active_thread = Qnil;
595
+ }
596
+
597
+ return Qnil;
598
+ }
599
+ #endif
600
+
601
+ /* call-seq:
602
+ * client.abandon_results!
603
+ *
604
+ * When using MULTI_STATEMENTS support, calling this will throw
605
+ * away any unprocessed results as fast as it can in order to
606
+ * put the connection back into a state where queries can be issued
607
+ * again.
608
+ */
609
+ static VALUE rb_mysql_client_abandon_results(VALUE self) {
610
+ MYSQL_RES *result;
611
+ int ret;
612
+
613
+ GET_CLIENT(self);
614
+
615
+ while (mysql_more_results(wrapper->client) == 1) {
616
+ ret = mysql_next_result(wrapper->client);
617
+ if (ret > 0) {
618
+ rb_raise_mysql2_error(wrapper);
619
+ }
620
+
621
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
622
+
623
+ if (result != NULL) {
624
+ mysql_free_result(result);
625
+ }
626
+ }
627
+
628
+ return Qnil;
629
+ }
630
+
631
+ /* call-seq:
632
+ * client.query(sql, options = {})
633
+ *
634
+ * Query the database with +sql+, with optional +options+. For the possible
635
+ * options, see @@default_query_options on the Mysql2::Client class.
636
+ */
637
+ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
638
+ #ifndef _WIN32
639
+ struct async_query_args async_args;
640
+ #endif
641
+ struct nogvl_send_query_args args;
642
+ int async = 0;
643
+ VALUE opts, current;
644
+ VALUE thread_current = rb_thread_current();
645
+ #ifdef HAVE_RUBY_ENCODING_H
646
+ rb_encoding *conn_enc;
647
+ #endif
648
+ GET_CLIENT(self);
649
+
650
+ REQUIRE_CONNECTED(wrapper);
651
+ args.mysql = wrapper->client;
652
+
653
+ current = rb_hash_dup(rb_iv_get(self, "@query_options"));
654
+ RB_GC_GUARD(current);
655
+ Check_Type(current, T_HASH);
656
+ rb_iv_set(self, "@current_query_options", current);
657
+
658
+ if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
659
+ rb_funcall(current, intern_merge_bang, 1, opts);
660
+
661
+ if (rb_hash_aref(current, sym_async) == Qtrue) {
662
+ async = 1;
663
+ }
664
+ }
665
+
666
+ Check_Type(args.sql, T_STRING);
667
+ #ifdef HAVE_RUBY_ENCODING_H
668
+ conn_enc = rb_to_encoding(wrapper->encoding);
669
+ /* ensure the string is in the encoding the connection is expecting */
670
+ args.sql = rb_str_export_to_enc(args.sql, conn_enc);
671
+ #endif
672
+ args.sql_ptr = StringValuePtr(args.sql);
673
+ args.sql_len = RSTRING_LEN(args.sql);
674
+
675
+ /* see if this connection is still waiting on a result from a previous query */
676
+ if (NIL_P(wrapper->active_thread)) {
677
+ /* mark this connection active */
678
+ wrapper->active_thread = thread_current;
679
+ } else if (wrapper->active_thread == thread_current) {
680
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
681
+ } else {
682
+ VALUE inspect = rb_inspect(wrapper->active_thread);
683
+ const char *thr = StringValueCStr(inspect);
684
+
685
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
686
+ RB_GC_GUARD(inspect);
687
+ }
688
+
689
+ args.wrapper = wrapper;
690
+
691
+ #ifndef _WIN32
692
+ rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
693
+
694
+ if (!async) {
695
+ async_args.fd = wrapper->client->net.fd;
696
+ async_args.self = self;
697
+
698
+ rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
699
+
700
+ return rb_mysql_client_async_result(self);
701
+ } else {
702
+ return Qnil;
703
+ }
704
+ #else
705
+ do_send_query(&args);
706
+
707
+ /* this will just block until the result is ready */
708
+ return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
709
+ #endif
710
+ }
711
+
712
+ /* call-seq:
713
+ * client.escape(string)
714
+ *
715
+ * Escape +string+ so that it may be used in a SQL statement.
716
+ */
717
+ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
718
+ unsigned char *newStr;
719
+ VALUE rb_str;
720
+ unsigned long newLen, oldLen;
721
+ #ifdef HAVE_RUBY_ENCODING_H
722
+ rb_encoding *default_internal_enc;
723
+ rb_encoding *conn_enc;
724
+ #endif
725
+ GET_CLIENT(self);
726
+
727
+ REQUIRE_CONNECTED(wrapper);
728
+ Check_Type(str, T_STRING);
729
+ #ifdef HAVE_RUBY_ENCODING_H
730
+ default_internal_enc = rb_default_internal_encoding();
731
+ conn_enc = rb_to_encoding(wrapper->encoding);
732
+ /* ensure the string is in the encoding the connection is expecting */
733
+ str = rb_str_export_to_enc(str, conn_enc);
734
+ #endif
735
+
736
+ oldLen = RSTRING_LEN(str);
737
+ newStr = xmalloc(oldLen*2+1);
738
+
739
+ newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
740
+ if (newLen == oldLen) {
741
+ /* no need to return a new ruby string if nothing changed */
742
+ xfree(newStr);
743
+ return str;
744
+ } else {
745
+ rb_str = rb_str_new((const char*)newStr, newLen);
746
+ #ifdef HAVE_RUBY_ENCODING_H
747
+ rb_enc_associate(rb_str, conn_enc);
748
+ if (default_internal_enc) {
749
+ rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
750
+ }
751
+ #endif
752
+ xfree(newStr);
753
+ return rb_str;
754
+ }
755
+ }
756
+
757
+ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
758
+ int result;
759
+ const void *retval = NULL;
760
+ unsigned int intval = 0;
761
+ const char * charval = NULL;
762
+ my_bool boolval;
763
+
764
+ GET_CLIENT(self);
765
+
766
+ REQUIRE_NOT_CONNECTED(wrapper);
767
+
768
+ if (NIL_P(value))
769
+ return Qfalse;
770
+
771
+ switch(opt) {
772
+ case MYSQL_OPT_CONNECT_TIMEOUT:
773
+ intval = NUM2UINT(value);
774
+ retval = &intval;
775
+ break;
776
+
777
+ case MYSQL_OPT_READ_TIMEOUT:
778
+ intval = NUM2UINT(value);
779
+ retval = &intval;
780
+ break;
781
+
782
+ case MYSQL_OPT_WRITE_TIMEOUT:
783
+ intval = NUM2UINT(value);
784
+ retval = &intval;
785
+ break;
786
+
787
+ case MYSQL_OPT_LOCAL_INFILE:
788
+ intval = (value == Qfalse ? 0 : 1);
789
+ retval = &intval;
790
+ break;
791
+
792
+ case MYSQL_OPT_RECONNECT:
793
+ boolval = (value == Qfalse ? 0 : 1);
794
+ retval = &boolval;
795
+ break;
796
+
797
+ case MYSQL_SECURE_AUTH:
798
+ boolval = (value == Qfalse ? 0 : 1);
799
+ retval = &boolval;
800
+ break;
801
+
802
+ case MYSQL_READ_DEFAULT_FILE:
803
+ charval = (const char *)StringValuePtr(value);
804
+ retval = charval;
805
+ break;
806
+
807
+ case MYSQL_READ_DEFAULT_GROUP:
808
+ charval = (const char *)StringValuePtr(value);
809
+ retval = charval;
810
+ break;
811
+
812
+ case MYSQL_INIT_COMMAND:
813
+ charval = (const char *)StringValuePtr(value);
814
+ retval = charval;
815
+ break;
816
+
817
+ default:
818
+ return Qfalse;
819
+ }
820
+
821
+ result = mysql_options(wrapper->client, opt, retval);
822
+
823
+ /* Zero means success */
824
+ if (result != 0) {
825
+ rb_warn("%s\n", mysql_error(wrapper->client));
826
+ } else {
827
+ /* Special case for options that are stored in the wrapper struct */
828
+ switch (opt) {
829
+ case MYSQL_OPT_RECONNECT:
830
+ wrapper->reconnect_enabled = boolval;
831
+ break;
832
+ case MYSQL_OPT_CONNECT_TIMEOUT:
833
+ wrapper->connect_timeout = intval;
834
+ break;
835
+ }
836
+ }
837
+
838
+ return (result == 0) ? Qtrue : Qfalse;
839
+ }
840
+
841
+ /* call-seq:
842
+ * client.info
843
+ *
844
+ * Returns a string that represents the client library version.
845
+ */
846
+ static VALUE rb_mysql_client_info(VALUE self) {
847
+ VALUE version, client_info;
848
+ #ifdef HAVE_RUBY_ENCODING_H
849
+ rb_encoding *default_internal_enc;
850
+ rb_encoding *conn_enc;
851
+ GET_CLIENT(self);
852
+ #endif
853
+ version = rb_hash_new();
854
+
855
+ #ifdef HAVE_RUBY_ENCODING_H
856
+ default_internal_enc = rb_default_internal_encoding();
857
+ conn_enc = rb_to_encoding(wrapper->encoding);
858
+ #endif
859
+
860
+ rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
861
+ client_info = rb_str_new2(mysql_get_client_info());
862
+ #ifdef HAVE_RUBY_ENCODING_H
863
+ rb_enc_associate(client_info, conn_enc);
864
+ if (default_internal_enc) {
865
+ client_info = rb_str_export_to_enc(client_info, default_internal_enc);
866
+ }
867
+ #endif
868
+ rb_hash_aset(version, sym_version, client_info);
869
+ return version;
870
+ }
871
+
872
+ /* call-seq:
873
+ * client.server_info
874
+ *
875
+ * Returns a string that represents the server version number
876
+ */
877
+ static VALUE rb_mysql_client_server_info(VALUE self) {
878
+ VALUE version, server_info;
879
+ #ifdef HAVE_RUBY_ENCODING_H
880
+ rb_encoding *default_internal_enc;
881
+ rb_encoding *conn_enc;
882
+ #endif
883
+ GET_CLIENT(self);
884
+
885
+ REQUIRE_CONNECTED(wrapper);
886
+ #ifdef HAVE_RUBY_ENCODING_H
887
+ default_internal_enc = rb_default_internal_encoding();
888
+ conn_enc = rb_to_encoding(wrapper->encoding);
889
+ #endif
890
+
891
+ version = rb_hash_new();
892
+ rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
893
+ server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
894
+ #ifdef HAVE_RUBY_ENCODING_H
895
+ rb_enc_associate(server_info, conn_enc);
896
+ if (default_internal_enc) {
897
+ server_info = rb_str_export_to_enc(server_info, default_internal_enc);
898
+ }
899
+ #endif
900
+ rb_hash_aset(version, sym_version, server_info);
901
+ return version;
902
+ }
903
+
904
+ /* call-seq:
905
+ * client.socket
906
+ *
907
+ * Return the file descriptor number for this client.
908
+ */
909
+ static VALUE rb_mysql_client_socket(VALUE self) {
910
+ GET_CLIENT(self);
911
+ #ifndef _WIN32
912
+ {
913
+ int fd_set_fd;
914
+ REQUIRE_CONNECTED(wrapper);
915
+ fd_set_fd = wrapper->client->net.fd;
916
+ return INT2NUM(fd_set_fd);
917
+ }
918
+ #else
919
+ rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
920
+ #endif
921
+ }
922
+
923
+ /* call-seq:
924
+ * client.last_id
925
+ *
926
+ * Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
927
+ * statement.
928
+ */
929
+ static VALUE rb_mysql_client_last_id(VALUE self) {
930
+ GET_CLIENT(self);
931
+ REQUIRE_CONNECTED(wrapper);
932
+ return ULL2NUM(mysql_insert_id(wrapper->client));
933
+ }
934
+
935
+ /* call-seq:
936
+ * client.affected_rows
937
+ *
938
+ * returns the number of rows changed, deleted, or inserted by the last statement
939
+ * if it was an UPDATE, DELETE, or INSERT.
940
+ */
941
+ static VALUE rb_mysql_client_affected_rows(VALUE self) {
942
+ my_ulonglong retVal;
943
+ GET_CLIENT(self);
944
+
945
+ REQUIRE_CONNECTED(wrapper);
946
+ retVal = mysql_affected_rows(wrapper->client);
947
+ if (retVal == (my_ulonglong)-1) {
948
+ rb_raise_mysql2_error(wrapper);
949
+ }
950
+ return ULL2NUM(retVal);
951
+ }
952
+
953
+ /* call-seq:
954
+ * client.thread_id
955
+ *
956
+ * Returns the thread ID of the current connection.
957
+ */
958
+ static VALUE rb_mysql_client_thread_id(VALUE self) {
959
+ unsigned long retVal;
960
+ GET_CLIENT(self);
961
+
962
+ REQUIRE_CONNECTED(wrapper);
963
+ retVal = mysql_thread_id(wrapper->client);
964
+ return ULL2NUM(retVal);
965
+ }
966
+
967
+ static void *nogvl_select_db(void *ptr) {
968
+ struct nogvl_select_db_args *args = ptr;
969
+
970
+ if (mysql_select_db(args->mysql, args->db) == 0)
971
+ return (void *)Qtrue;
972
+ else
973
+ return (void *)Qfalse;
974
+ }
975
+
976
+ /* call-seq:
977
+ * client.select_db(name)
978
+ *
979
+ * Causes the database specified by +name+ to become the default (current)
980
+ * database on the connection specified by mysql.
981
+ */
982
+ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
983
+ {
984
+ struct nogvl_select_db_args args;
985
+
986
+ GET_CLIENT(self);
987
+ REQUIRE_CONNECTED(wrapper);
988
+
989
+ args.mysql = wrapper->client;
990
+ args.db = StringValuePtr(db);
991
+
992
+ if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
993
+ rb_raise_mysql2_error(wrapper);
994
+
995
+ return db;
996
+ }
997
+
998
+ static void *nogvl_ping(void *ptr) {
999
+ MYSQL *client = ptr;
1000
+
1001
+ return (void *)(mysql_ping(client) == 0 ? Qtrue : Qfalse);
1002
+ }
1003
+
1004
+ /* call-seq:
1005
+ * client.ping
1006
+ *
1007
+ * Checks whether the connection to the server is working. If the connection
1008
+ * has gone down and auto-reconnect is enabled an attempt to reconnect is made.
1009
+ * If the connection is down and auto-reconnect is disabled, ping returns an
1010
+ * error.
1011
+ */
1012
+ static VALUE rb_mysql_client_ping(VALUE self) {
1013
+ GET_CLIENT(self);
1014
+
1015
+ if (!wrapper->connected) {
1016
+ return Qfalse;
1017
+ } else {
1018
+ return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1019
+ }
1020
+ }
1021
+
1022
+ /* call-seq:
1023
+ * client.more_results?
1024
+ *
1025
+ * Returns true or false if there are more results to process.
1026
+ */
1027
+ static VALUE rb_mysql_client_more_results(VALUE self)
1028
+ {
1029
+ GET_CLIENT(self);
1030
+ if (mysql_more_results(wrapper->client) == 0)
1031
+ return Qfalse;
1032
+ else
1033
+ return Qtrue;
1034
+ }
1035
+
1036
+ /* call-seq:
1037
+ * client.next_result
1038
+ *
1039
+ * Fetch the next result set from the server.
1040
+ * Returns nothing.
1041
+ */
1042
+ static VALUE rb_mysql_client_next_result(VALUE self)
1043
+ {
1044
+ int ret;
1045
+ GET_CLIENT(self);
1046
+ ret = mysql_next_result(wrapper->client);
1047
+ if (ret > 0) {
1048
+ rb_raise_mysql2_error(wrapper);
1049
+ return Qfalse;
1050
+ } else if (ret == 0) {
1051
+ return Qtrue;
1052
+ } else {
1053
+ return Qfalse;
1054
+ }
1055
+ }
1056
+
1057
+ /* call-seq:
1058
+ * client.store_result
1059
+ *
1060
+ * Return the next result object from a query which
1061
+ * yielded multiple result sets.
1062
+ */
1063
+ static VALUE rb_mysql_client_store_result(VALUE self)
1064
+ {
1065
+ MYSQL_RES * result;
1066
+ VALUE resultObj;
1067
+ VALUE current;
1068
+ GET_CLIENT(self);
1069
+
1070
+ result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
1071
+
1072
+ if (result == NULL) {
1073
+ if (mysql_errno(wrapper->client) != 0) {
1074
+ rb_raise_mysql2_error(wrapper);
1075
+ }
1076
+ /* no data and no error, so query was not a SELECT */
1077
+ return Qnil;
1078
+ }
1079
+
1080
+ current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1081
+ RB_GC_GUARD(current);
1082
+ Check_Type(current, T_HASH);
1083
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1084
+
1085
+ return resultObj;
1086
+ }
1087
+
1088
+ #ifdef HAVE_RUBY_ENCODING_H
1089
+ /* call-seq:
1090
+ * client.encoding
1091
+ *
1092
+ * Returns the encoding set on the client.
1093
+ */
1094
+ static VALUE rb_mysql_client_encoding(VALUE self) {
1095
+ GET_CLIENT(self);
1096
+ return wrapper->encoding;
1097
+ }
1098
+ #endif
1099
+
1100
+ /* call-seq:
1101
+ * client.reconnect = true
1102
+ *
1103
+ * Enable or disable the automatic reconnect behavior of libmysql.
1104
+ * Read http://dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html
1105
+ * for more information.
1106
+ */
1107
+ static VALUE set_reconnect(VALUE self, VALUE value) {
1108
+ return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
1109
+ }
1110
+
1111
+ static VALUE set_local_infile(VALUE self, VALUE value) {
1112
+ return _mysql_client_options(self, MYSQL_OPT_LOCAL_INFILE, value);
1113
+ }
1114
+
1115
+ static VALUE set_connect_timeout(VALUE self, VALUE value) {
1116
+ long int sec;
1117
+ Check_Type(value, T_FIXNUM);
1118
+ sec = FIX2INT(value);
1119
+ if (sec < 0) {
1120
+ rb_raise(cMysql2Error, "connect_timeout must be a positive integer, you passed %ld", sec);
1121
+ }
1122
+ return _mysql_client_options(self, MYSQL_OPT_CONNECT_TIMEOUT, value);
1123
+ }
1124
+
1125
+ static VALUE set_read_timeout(VALUE self, VALUE value) {
1126
+ long int sec;
1127
+ Check_Type(value, T_FIXNUM);
1128
+ sec = FIX2INT(value);
1129
+ if (sec < 0) {
1130
+ rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
1131
+ }
1132
+ /* Set the instance variable here even though _mysql_client_options
1133
+ might not succeed, because the timeout is used in other ways
1134
+ elsewhere */
1135
+ rb_iv_set(self, "@read_timeout", value);
1136
+ return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1137
+ }
1138
+
1139
+ static VALUE set_write_timeout(VALUE self, VALUE value) {
1140
+ long int sec;
1141
+ Check_Type(value, T_FIXNUM);
1142
+ sec = FIX2INT(value);
1143
+ if (sec < 0) {
1144
+ rb_raise(cMysql2Error, "write_timeout must be a positive integer, you passed %ld", sec);
1145
+ }
1146
+ return _mysql_client_options(self, MYSQL_OPT_WRITE_TIMEOUT, value);
1147
+ }
1148
+
1149
+ static VALUE set_charset_name(VALUE self, VALUE value) {
1150
+ char *charset_name;
1151
+ #ifdef HAVE_RUBY_ENCODING_H
1152
+ size_t charset_name_len;
1153
+ const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1154
+ rb_encoding *enc;
1155
+ VALUE rb_enc;
1156
+ #endif
1157
+ GET_CLIENT(self);
1158
+
1159
+ charset_name = RSTRING_PTR(value);
1160
+
1161
+ #ifdef HAVE_RUBY_ENCODING_H
1162
+ charset_name_len = RSTRING_LEN(value);
1163
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
1164
+ if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1165
+ VALUE inspect = rb_inspect(value);
1166
+ rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
1167
+ } else {
1168
+ enc = rb_enc_find(mysql2rb->rb_name);
1169
+ rb_enc = rb_enc_from_encoding(enc);
1170
+ wrapper->encoding = rb_enc;
1171
+ }
1172
+ #endif
1173
+
1174
+ if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1175
+ /* TODO: warning - unable to set charset */
1176
+ rb_warn("%s\n", mysql_error(wrapper->client));
1177
+ }
1178
+
1179
+ return value;
1180
+ }
1181
+
1182
+ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
1183
+ GET_CLIENT(self);
1184
+
1185
+ mysql_ssl_set(wrapper->client,
1186
+ NIL_P(key) ? NULL : StringValuePtr(key),
1187
+ NIL_P(cert) ? NULL : StringValuePtr(cert),
1188
+ NIL_P(ca) ? NULL : StringValuePtr(ca),
1189
+ NIL_P(capath) ? NULL : StringValuePtr(capath),
1190
+ NIL_P(cipher) ? NULL : StringValuePtr(cipher));
1191
+
1192
+ return self;
1193
+ }
1194
+
1195
+ static VALUE set_secure_auth(VALUE self, VALUE value) {
1196
+ return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1197
+ }
1198
+
1199
+ static VALUE set_read_default_file(VALUE self, VALUE value) {
1200
+ return _mysql_client_options(self, MYSQL_READ_DEFAULT_FILE, value);
1201
+ }
1202
+
1203
+ static VALUE set_read_default_group(VALUE self, VALUE value) {
1204
+ return _mysql_client_options(self, MYSQL_READ_DEFAULT_GROUP, value);
1205
+ }
1206
+
1207
+ static VALUE set_init_command(VALUE self, VALUE value) {
1208
+ return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1209
+ }
1210
+
1211
+ static VALUE initialize_ext(VALUE self) {
1212
+ GET_CLIENT(self);
1213
+
1214
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1215
+ /* TODO: warning - not enough memory? */
1216
+ return rb_raise_mysql2_error(wrapper);
1217
+ }
1218
+
1219
+ wrapper->initialized = 1;
1220
+ return self;
1221
+ }
1222
+
1223
+ void init_mysql2_client() {
1224
+ /* verify the libmysql we're about to use was the version we were built against
1225
+ https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
1226
+ int i;
1227
+ int dots = 0;
1228
+ const char *lib = mysql_get_client_info();
1229
+
1230
+ for (i = 0; lib[i] != 0 && MYSQL_LINK_VERSION[i] != 0; i++) {
1231
+ if (lib[i] == '.') {
1232
+ dots++;
1233
+ /* we only compare MAJOR and MINOR */
1234
+ if (dots == 2) break;
1235
+ }
1236
+ if (lib[i] != MYSQL_LINK_VERSION[i]) {
1237
+ rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
1238
+ return;
1239
+ }
1240
+ }
1241
+
1242
+ /* Initializing mysql library, so different threads could call Client.new */
1243
+ /* without race condition in the library */
1244
+ if (mysql_library_init(0, NULL, NULL) != 0) {
1245
+ rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1246
+ return;
1247
+ }
1248
+
1249
+ #if 0
1250
+ mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1251
+ #endif
1252
+ cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1253
+
1254
+ rb_define_alloc_func(cMysql2Client, allocate);
1255
+
1256
+ rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
1257
+
1258
+ rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1259
+ rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1260
+ rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1261
+ rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1262
+ rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1263
+ rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
1264
+ rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
1265
+ rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
1266
+ rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
1267
+ rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1268
+ rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1269
+ rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1270
+ rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1271
+ rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1272
+ rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1273
+ rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1274
+ rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1275
+ rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1276
+ rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1277
+ #ifdef HAVE_RUBY_ENCODING_H
1278
+ rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1279
+ #endif
1280
+
1281
+ rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1282
+ rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
1283
+ rb_define_private_method(cMysql2Client, "write_timeout=", set_write_timeout, 1);
1284
+ rb_define_private_method(cMysql2Client, "local_infile=", set_local_infile, 1);
1285
+ rb_define_private_method(cMysql2Client, "charset_name=", set_charset_name, 1);
1286
+ rb_define_private_method(cMysql2Client, "secure_auth=", set_secure_auth, 1);
1287
+ rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1288
+ rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1289
+ rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1290
+ rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1291
+ rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1292
+ rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1293
+
1294
+ sym_id = ID2SYM(rb_intern("id"));
1295
+ sym_version = ID2SYM(rb_intern("version"));
1296
+ sym_async = ID2SYM(rb_intern("async"));
1297
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1298
+ sym_as = ID2SYM(rb_intern("as"));
1299
+ sym_array = ID2SYM(rb_intern("array"));
1300
+ sym_stream = ID2SYM(rb_intern("stream"));
1301
+
1302
+ intern_merge = rb_intern("merge");
1303
+ intern_merge_bang = rb_intern("merge!");
1304
+ intern_error_number_eql = rb_intern("error_number=");
1305
+ intern_sql_state_eql = rb_intern("sql_state=");
1306
+
1307
+ #ifdef CLIENT_LONG_PASSWORD
1308
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1309
+ LONG2NUM(CLIENT_LONG_PASSWORD));
1310
+ #endif
1311
+
1312
+ #ifdef CLIENT_FOUND_ROWS
1313
+ rb_const_set(cMysql2Client, rb_intern("FOUND_ROWS"),
1314
+ LONG2NUM(CLIENT_FOUND_ROWS));
1315
+ #endif
1316
+
1317
+ #ifdef CLIENT_LONG_FLAG
1318
+ rb_const_set(cMysql2Client, rb_intern("LONG_FLAG"),
1319
+ LONG2NUM(CLIENT_LONG_FLAG));
1320
+ #endif
1321
+
1322
+ #ifdef CLIENT_CONNECT_WITH_DB
1323
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_WITH_DB"),
1324
+ LONG2NUM(CLIENT_CONNECT_WITH_DB));
1325
+ #endif
1326
+
1327
+ #ifdef CLIENT_NO_SCHEMA
1328
+ rb_const_set(cMysql2Client, rb_intern("NO_SCHEMA"),
1329
+ LONG2NUM(CLIENT_NO_SCHEMA));
1330
+ #endif
1331
+
1332
+ #ifdef CLIENT_COMPRESS
1333
+ rb_const_set(cMysql2Client, rb_intern("COMPRESS"), LONG2NUM(CLIENT_COMPRESS));
1334
+ #endif
1335
+
1336
+ #ifdef CLIENT_ODBC
1337
+ rb_const_set(cMysql2Client, rb_intern("ODBC"), LONG2NUM(CLIENT_ODBC));
1338
+ #endif
1339
+
1340
+ #ifdef CLIENT_LOCAL_FILES
1341
+ rb_const_set(cMysql2Client, rb_intern("LOCAL_FILES"),
1342
+ LONG2NUM(CLIENT_LOCAL_FILES));
1343
+ #endif
1344
+
1345
+ #ifdef CLIENT_IGNORE_SPACE
1346
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SPACE"),
1347
+ LONG2NUM(CLIENT_IGNORE_SPACE));
1348
+ #endif
1349
+
1350
+ #ifdef CLIENT_PROTOCOL_41
1351
+ rb_const_set(cMysql2Client, rb_intern("PROTOCOL_41"),
1352
+ LONG2NUM(CLIENT_PROTOCOL_41));
1353
+ #endif
1354
+
1355
+ #ifdef CLIENT_INTERACTIVE
1356
+ rb_const_set(cMysql2Client, rb_intern("INTERACTIVE"),
1357
+ LONG2NUM(CLIENT_INTERACTIVE));
1358
+ #endif
1359
+
1360
+ #ifdef CLIENT_SSL
1361
+ rb_const_set(cMysql2Client, rb_intern("SSL"), LONG2NUM(CLIENT_SSL));
1362
+ #endif
1363
+
1364
+ #ifdef CLIENT_IGNORE_SIGPIPE
1365
+ rb_const_set(cMysql2Client, rb_intern("IGNORE_SIGPIPE"),
1366
+ LONG2NUM(CLIENT_IGNORE_SIGPIPE));
1367
+ #endif
1368
+
1369
+ #ifdef CLIENT_TRANSACTIONS
1370
+ rb_const_set(cMysql2Client, rb_intern("TRANSACTIONS"),
1371
+ LONG2NUM(CLIENT_TRANSACTIONS));
1372
+ #endif
1373
+
1374
+ #ifdef CLIENT_RESERVED
1375
+ rb_const_set(cMysql2Client, rb_intern("RESERVED"), LONG2NUM(CLIENT_RESERVED));
1376
+ #endif
1377
+
1378
+ #ifdef CLIENT_SECURE_CONNECTION
1379
+ rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"),
1380
+ LONG2NUM(CLIENT_SECURE_CONNECTION));
1381
+ #else
1382
+ /* HACK because MySQL5.7 no longer defines this constant,
1383
+ * but we're using it in our default connection flags. */
1384
+ rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1385
+ #endif
1386
+
1387
+ #ifdef CLIENT_MULTI_STATEMENTS
1388
+ rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1389
+ LONG2NUM(CLIENT_MULTI_STATEMENTS));
1390
+ #endif
1391
+
1392
+ #ifdef CLIENT_PS_MULTI_RESULTS
1393
+ rb_const_set(cMysql2Client, rb_intern("PS_MULTI_RESULTS"),
1394
+ LONG2NUM(CLIENT_PS_MULTI_RESULTS));
1395
+ #endif
1396
+
1397
+ #ifdef CLIENT_SSL_VERIFY_SERVER_CERT
1398
+ rb_const_set(cMysql2Client, rb_intern("SSL_VERIFY_SERVER_CERT"),
1399
+ LONG2NUM(CLIENT_SSL_VERIFY_SERVER_CERT));
1400
+ #endif
1401
+
1402
+ #ifdef CLIENT_REMEMBER_OPTIONS
1403
+ rb_const_set(cMysql2Client, rb_intern("REMEMBER_OPTIONS"),
1404
+ LONG2NUM(CLIENT_REMEMBER_OPTIONS));
1405
+ #endif
1406
+
1407
+ #ifdef CLIENT_ALL_FLAGS
1408
+ rb_const_set(cMysql2Client, rb_intern("ALL_FLAGS"),
1409
+ LONG2NUM(CLIENT_ALL_FLAGS));
1410
+ #endif
1411
+
1412
+ #ifdef CLIENT_BASIC_FLAGS
1413
+ rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1414
+ LONG2NUM(CLIENT_BASIC_FLAGS));
1415
+ #endif
1416
+ }