mysql2 0.3.18 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +132 -55
  5. data/examples/eventmachine.rb +1 -1
  6. data/examples/threaded.rb +4 -6
  7. data/ext/mysql2/client.c +374 -197
  8. data/ext/mysql2/client.h +13 -3
  9. data/ext/mysql2/extconf.rb +119 -35
  10. data/ext/mysql2/infile.c +2 -2
  11. data/ext/mysql2/mysql2_ext.c +1 -0
  12. data/ext/mysql2/mysql2_ext.h +7 -6
  13. data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
  14. data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
  15. data/ext/mysql2/result.c +512 -138
  16. data/ext/mysql2/result.h +13 -6
  17. data/ext/mysql2/statement.c +595 -0
  18. data/ext/mysql2/statement.h +19 -0
  19. data/lib/mysql2/client.rb +85 -26
  20. data/lib/mysql2/console.rb +1 -1
  21. data/lib/mysql2/em.rb +5 -6
  22. data/lib/mysql2/error.rb +18 -27
  23. data/lib/mysql2/field.rb +3 -0
  24. data/lib/mysql2/statement.rb +17 -0
  25. data/lib/mysql2/version.rb +1 -1
  26. data/lib/mysql2.rb +38 -18
  27. data/spec/configuration.yml.example +0 -6
  28. data/spec/em/em_spec.rb +22 -21
  29. data/spec/mysql2/client_spec.rb +530 -388
  30. data/spec/mysql2/error_spec.rb +38 -39
  31. data/spec/mysql2/result_spec.rb +223 -214
  32. data/spec/mysql2/statement_spec.rb +763 -0
  33. data/spec/spec_helper.rb +80 -59
  34. data/spec/ssl/ca-cert.pem +17 -0
  35. data/spec/ssl/ca-key.pem +27 -0
  36. data/spec/ssl/ca.cnf +22 -0
  37. data/spec/ssl/cert.cnf +22 -0
  38. data/spec/ssl/client-cert.pem +17 -0
  39. data/spec/ssl/client-key.pem +27 -0
  40. data/spec/ssl/client-req.pem +15 -0
  41. data/spec/ssl/gen_certs.sh +48 -0
  42. data/spec/ssl/pkcs8-client-key.pem +28 -0
  43. data/spec/ssl/pkcs8-server-key.pem +28 -0
  44. data/spec/ssl/server-cert.pem +17 -0
  45. data/spec/ssl/server-key.pem +27 -0
  46. data/spec/ssl/server-req.pem +15 -0
  47. data/support/mysql_enc_to_ruby.rb +7 -8
  48. data/support/ruby_enc_to_mysql.rb +1 -1
  49. metadata +42 -47
data/ext/mysql2/client.c CHANGED
@@ -16,12 +16,12 @@
16
16
 
17
17
  VALUE cMysql2Client;
18
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;
19
+ static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
20
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
21
 
22
22
  #ifndef HAVE_RB_HASH_DUP
23
- static VALUE rb_hash_dup(VALUE other) {
24
- return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
23
+ VALUE rb_hash_dup(VALUE other) {
24
+ return rb_funcall(rb_cHash, intern_brackets, 1, other);
25
25
  }
26
26
  #endif
27
27
 
@@ -30,25 +30,24 @@ static VALUE rb_hash_dup(VALUE other) {
30
30
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
31
  }
32
32
 
33
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
34
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
35
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
36
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
37
+ #endif
38
+
33
39
  #define REQUIRE_CONNECTED(wrapper) \
34
40
  REQUIRE_INITIALIZED(wrapper) \
35
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36
- rb_raise(cMysql2Error, "closed MySQL connection"); \
41
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
42
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
37
43
  }
38
44
 
39
45
  #define REQUIRE_NOT_CONNECTED(wrapper) \
40
46
  REQUIRE_INITIALIZED(wrapper) \
41
- if (wrapper->connected) { \
47
+ if (CONNECTED(wrapper)) { \
42
48
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
43
49
  }
44
50
 
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
51
  /*
53
52
  * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
54
53
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
@@ -60,6 +59,16 @@ static VALUE rb_hash_dup(VALUE other) {
60
59
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
61
60
  #endif
62
61
 
62
+ /*
63
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
64
+ */
65
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
66
+ #define SSL_MODE_DISABLED 1
67
+ #define SSL_MODE_REQUIRED 3
68
+ #define HAVE_CONST_SSL_MODE_DISABLED
69
+ #define HAVE_CONST_SSL_MODE_REQUIRED
70
+ #endif
71
+
63
72
  /*
64
73
  * used to pass all arguments to mysql_real_connect while inside
65
74
  * rb_thread_call_without_gvl
@@ -96,6 +105,42 @@ struct nogvl_select_db_args {
96
105
  char *db;
97
106
  };
98
107
 
108
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
109
+ unsigned long version = mysql_get_client_version();
110
+
111
+ if (version < 50703) {
112
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
113
+ return Qnil;
114
+ }
115
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
116
+ GET_CLIENT(self);
117
+ int val = NUM2INT( setting );
118
+ if (version >= 50703 && version < 50711) {
119
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
120
+ bool b = ( val == SSL_MODE_REQUIRED );
121
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
122
+ return INT2NUM(result);
123
+ } else {
124
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
125
+ return Qnil;
126
+ }
127
+ }
128
+ #endif
129
+ #ifdef FULL_SSL_MODE_SUPPORT
130
+ GET_CLIENT(self);
131
+ int val = NUM2INT( setting );
132
+
133
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
134
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
135
+ }
136
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
137
+
138
+ return INT2NUM(result);
139
+ #endif
140
+ #ifdef NO_SSL_MODE_SUPPORT
141
+ return Qnil;
142
+ #endif
143
+ }
99
144
  /*
100
145
  * non-blocking mysql_*() functions that we won't be wrapping since
101
146
  * they do not appear to hit the network nor issue any interruptible
@@ -136,16 +181,17 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
136
181
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137
182
  #endif
138
183
 
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);
184
+ e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
185
+ rb_error_msg,
186
+ LONG2FIX(wrapper->server_version),
187
+ UINT2NUM(mysql_errno(wrapper->client)),
188
+ rb_sql_state);
142
189
  rb_exc_raise(e);
143
- return Qnil;
144
190
  }
145
191
 
146
192
  static void *nogvl_init(void *ptr) {
147
193
  MYSQL *client;
148
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
194
+ mysql_client_wrapper *wrapper = ptr;
149
195
 
150
196
  /* may initialize embedded server and read /etc/services off disk */
151
197
  client = mysql_init(wrapper->client);
@@ -182,23 +228,31 @@ static void *nogvl_connect(void *ptr) {
182
228
  */
183
229
  static VALUE invalidate_fd(int clientfd)
184
230
  {
185
- #ifdef SOCK_CLOEXEC
231
+ #ifdef O_CLOEXEC
186
232
  /* Atomically set CLOEXEC on the new FD in case another thread forks */
187
233
  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
234
  #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);
235
+ /* Well we don't have O_CLOEXEC, trigger the fallback code below */
236
+ int sockfd = -1;
197
237
  #endif
198
238
 
199
239
  if (sockfd < 0) {
200
- /*
201
- * Cannot raise here, because one or both of the following may be true:
240
+ /* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
241
+ * compile time, but isn't available at run-time. So we'll just be quick
242
+ * about setting FD_CLOEXEC now.
243
+ */
244
+ int flags;
245
+ sockfd = open("/dev/null", O_RDWR);
246
+ flags = fcntl(sockfd, F_GETFD);
247
+ /* Do the flags dance in case there are more defined flags in the future */
248
+ if (flags != -1) {
249
+ flags |= FD_CLOEXEC;
250
+ fcntl(sockfd, F_SETFD, flags);
251
+ }
252
+ }
253
+
254
+ if (sockfd < 0) {
255
+ /* Cannot raise here, because one or both of the following may be true:
202
256
  * a) we have no GVL (in C Ruby)
203
257
  * b) are running as a GC finalizer
204
258
  */
@@ -213,41 +267,46 @@ static VALUE invalidate_fd(int clientfd)
213
267
  #endif /* _WIN32 */
214
268
 
215
269
  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
270
+ mysql_client_wrapper *wrapper = ptr;
235
271
 
236
- mysql_close(wrapper->client); /* only used to free memory at this point */
272
+ if (!wrapper->closed) {
273
+ mysql_close(wrapper->client);
274
+ wrapper->closed = 1;
275
+ wrapper->reconnect_enabled = 0;
276
+ wrapper->active_thread = Qnil;
237
277
  }
238
278
 
239
279
  return NULL;
240
280
  }
241
281
 
282
+ /* this is called during GC */
242
283
  static void rb_mysql_client_free(void *ptr) {
243
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
284
+ mysql_client_wrapper *wrapper = ptr;
244
285
  decr_mysql2_client(wrapper);
245
286
  }
246
287
 
247
288
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
248
289
  {
249
290
  wrapper->refcount--;
291
+
250
292
  if (wrapper->refcount == 0) {
293
+ #ifndef _WIN32
294
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
295
+ /* The client is being garbage collected while connected. Prevent
296
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
297
+ * the socket by invalidating it. invalidate_fd() will drop this
298
+ * process's reference to the socket only, while a QUIT or shutdown()
299
+ * would render the underlying connection unusable, interrupting other
300
+ * processes which share this object across a fork().
301
+ */
302
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
303
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
304
+ close(wrapper->client->net.fd);
305
+ }
306
+ wrapper->client->net.fd = -1;
307
+ }
308
+ #endif
309
+
251
310
  nogvl_close(wrapper);
252
311
  xfree(wrapper->client);
253
312
  xfree(wrapper);
@@ -260,13 +319,15 @@ static VALUE allocate(VALUE klass) {
260
319
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
320
  wrapper->encoding = Qnil;
262
321
  wrapper->active_thread = Qnil;
322
+ wrapper->automatic_close = 1;
263
323
  wrapper->server_version = 0;
264
324
  wrapper->reconnect_enabled = 0;
265
325
  wrapper->connect_timeout = 0;
266
- wrapper->connected = 0; /* means that a database connection is open */
267
326
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
268
327
  wrapper->refcount = 1;
328
+ wrapper->closed = 0;
269
329
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
330
+
270
331
  return obj;
271
332
  }
272
333
 
@@ -287,7 +348,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
287
348
  oldLen = RSTRING_LEN(str);
288
349
  newStr = xmalloc(oldLen*2+1);
289
350
 
290
- newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
351
+ newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
291
352
  if (newLen == oldLen) {
292
353
  /* no need to return a new ruby string if nothing changed */
293
354
  xfree(newStr);
@@ -330,20 +391,39 @@ static VALUE rb_mysql_info(VALUE self) {
330
391
  return rb_str;
331
392
  }
332
393
 
394
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
395
+ {
396
+ const char *cipher;
397
+ VALUE rb_str;
398
+ GET_CLIENT(self);
399
+
400
+ cipher = mysql_get_ssl_cipher(wrapper->client);
401
+
402
+ if (cipher == NULL) {
403
+ return Qnil;
404
+ }
405
+
406
+ rb_str = rb_str_new2(cipher);
407
+ #ifdef HAVE_RUBY_ENCODING_H
408
+ rb_enc_associate(rb_str, rb_utf8_encoding());
409
+ #endif
410
+
411
+ return rb_str;
412
+ }
413
+
333
414
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
334
415
  struct nogvl_connect_args args;
335
- time_t start_time, end_time;
336
- unsigned int elapsed_time, connect_timeout;
416
+ time_t start_time, end_time, elapsed_time, connect_timeout;
337
417
  VALUE rv;
338
418
  GET_CLIENT(self);
339
419
 
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;
420
+ args.host = NIL_P(host) ? NULL : StringValueCStr(host);
421
+ args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
422
+ args.port = NIL_P(port) ? 0 : NUM2INT(port);
423
+ args.user = NIL_P(user) ? NULL : StringValueCStr(user);
424
+ args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
425
+ args.db = NIL_P(database) ? NULL : StringValueCStr(database);
426
+ args.mysql = wrapper->client;
347
427
  args.client_flag = NUM2ULONG(flags);
348
428
 
349
429
  if (wrapper->connect_timeout)
@@ -360,7 +440,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
360
440
  /* avoid an early timeout due to time truncating milliseconds off the start time */
361
441
  if (elapsed_time > 0)
362
442
  elapsed_time--;
363
- if (elapsed_time >= wrapper->connect_timeout)
443
+ if (elapsed_time >= (time_t)wrapper->connect_timeout)
364
444
  break;
365
445
  connect_timeout = wrapper->connect_timeout - elapsed_time;
366
446
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
@@ -372,30 +452,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
452
  if (wrapper->connect_timeout)
373
453
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
454
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
455
+ rb_raise_mysql2_error(wrapper);
376
456
  }
377
457
 
378
458
  wrapper->server_version = mysql_get_server_version(wrapper->client);
379
- wrapper->connected = 1;
380
459
  return self;
381
460
  }
382
461
 
383
462
  /*
384
- * Immediately disconnect from the server, normally the garbage collector
463
+ * Immediately disconnect from the server; normally the garbage collector
385
464
  * will disconnect automatically when a connection is no longer needed.
386
465
  * Explicitly closing this will free up server resources sooner than waiting
387
466
  * for the garbage collector.
467
+ *
468
+ * @return [nil]
388
469
  */
389
470
  static VALUE rb_mysql_client_close(VALUE self) {
390
471
  GET_CLIENT(self);
391
472
 
392
- if (wrapper->connected) {
473
+ if (wrapper->client) {
393
474
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
394
475
  }
395
476
 
396
477
  return Qnil;
397
478
  }
398
479
 
480
+ /* call-seq:
481
+ * client.closed?
482
+ *
483
+ * @return [Boolean]
484
+ */
485
+ static VALUE rb_mysql_client_closed(VALUE self) {
486
+ GET_CLIENT(self);
487
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
488
+ }
489
+
399
490
  /*
400
491
  * mysql_send_query is unlikely to block since most queries are small
401
492
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -415,8 +506,8 @@ static VALUE do_send_query(void *args) {
415
506
  mysql_client_wrapper *wrapper = query_args->wrapper;
416
507
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
417
508
  /* an error occurred, we're not active anymore */
418
- MARK_CONN_INACTIVE(self);
419
- return rb_raise_mysql2_error(wrapper);
509
+ wrapper->active_thread = Qnil;
510
+ rb_raise_mysql2_error(wrapper);
420
511
  }
421
512
  return Qnil;
422
513
  }
@@ -428,16 +519,15 @@ static VALUE do_send_query(void *args) {
428
519
  */
429
520
  static void *nogvl_read_query_result(void *ptr) {
430
521
  MYSQL * client = ptr;
431
- my_bool res = mysql_read_query_result(client);
522
+ bool res = mysql_read_query_result(client);
432
523
 
433
524
  return (void *)(res == 0 ? Qtrue : Qfalse);
434
525
  }
435
526
 
436
527
  static void *nogvl_do_result(void *ptr, char use_result) {
437
- mysql_client_wrapper *wrapper;
528
+ mysql_client_wrapper *wrapper = ptr;
438
529
  MYSQL_RES *result;
439
530
 
440
- wrapper = (mysql_client_wrapper *)ptr;
441
531
  if (use_result) {
442
532
  result = mysql_use_result(wrapper->client);
443
533
  } else {
@@ -478,8 +568,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
478
568
  REQUIRE_CONNECTED(wrapper);
479
569
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
480
570
  /* an error occurred, mark this connection inactive */
481
- MARK_CONN_INACTIVE(self);
482
- return rb_raise_mysql2_error(wrapper);
571
+ wrapper->active_thread = Qnil;
572
+ rb_raise_mysql2_error(wrapper);
483
573
  }
484
574
 
485
575
  is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
@@ -491,7 +581,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
491
581
 
492
582
  if (result == NULL) {
493
583
  if (mysql_errno(wrapper->client) != 0) {
494
- MARK_CONN_INACTIVE(self);
584
+ wrapper->active_thread = Qnil;
495
585
  rb_raise_mysql2_error(wrapper);
496
586
  }
497
587
  /* no data and no error, so query was not a SELECT */
@@ -499,9 +589,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
499
589
  }
500
590
 
501
591
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
502
- RB_GC_GUARD(current);
592
+ (void)RB_GC_GUARD(current);
503
593
  Check_Type(current, T_HASH);
504
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
594
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
505
595
 
506
596
  return resultObj;
507
597
  }
@@ -516,30 +606,29 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
516
606
  GET_CLIENT(self);
517
607
 
518
608
  wrapper->active_thread = Qnil;
519
- wrapper->connected = 0;
520
609
 
521
610
  /* Invalidate the MySQL socket to prevent further communication.
522
611
  * The GC will come along later and call mysql_close to free it.
523
612
  */
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);
613
+ if (CONNECTED(wrapper)) {
614
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
615
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
616
+ close(wrapper->client->net.fd);
617
+ }
618
+ wrapper->client->net.fd = -1;
527
619
  }
528
620
 
529
621
  rb_exc_raise(error);
530
-
531
- return Qnil;
532
622
  }
533
623
 
534
624
  static VALUE do_query(void *args) {
535
- struct async_query_args *async_args;
625
+ struct async_query_args *async_args = args;
536
626
  struct timeval tv;
537
- struct timeval* tvp;
627
+ struct timeval *tvp;
538
628
  long int sec;
539
629
  int retval;
540
630
  VALUE read_timeout;
541
631
 
542
- async_args = (struct async_query_args *)args;
543
632
  read_timeout = rb_iv_get(async_args->self, "@read_timeout");
544
633
 
545
634
  tvp = NULL;
@@ -575,28 +664,50 @@ static VALUE do_query(void *args) {
575
664
 
576
665
  return Qnil;
577
666
  }
578
- #else
579
- static VALUE finish_and_mark_inactive(void *args) {
580
- VALUE self;
581
- MYSQL_RES *result;
582
-
583
- self = (VALUE)args;
667
+ #endif
584
668
 
669
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
585
670
  GET_CLIENT(self);
586
671
 
672
+ /* Check if execution terminated while result was still being read. */
587
673
  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
-
674
+ if (CONNECTED(wrapper)) {
675
+ /* Invalidate the MySQL socket to prevent further communication. */
676
+ #ifndef _WIN32
677
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
678
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
679
+ close(wrapper->client->net.fd);
680
+ }
681
+ #else
682
+ close(wrapper->client->net.fd);
683
+ #endif
684
+ wrapper->client->net.fd = -1;
685
+ }
686
+ /* Skip mysql client check performed before command execution. */
687
+ wrapper->client->status = MYSQL_STATUS_READY;
594
688
  wrapper->active_thread = Qnil;
595
689
  }
596
690
 
597
691
  return Qnil;
598
692
  }
599
- #endif
693
+
694
+ void rb_mysql_client_set_active_thread(VALUE self) {
695
+ VALUE thread_current = rb_thread_current();
696
+ GET_CLIENT(self);
697
+
698
+ // see if this connection is still waiting on a result from a previous query
699
+ if (NIL_P(wrapper->active_thread)) {
700
+ // mark this connection active
701
+ wrapper->active_thread = thread_current;
702
+ } else if (wrapper->active_thread == thread_current) {
703
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
704
+ } else {
705
+ VALUE inspect = rb_inspect(wrapper->active_thread);
706
+ const char *thr = StringValueCStr(inspect);
707
+
708
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
709
+ }
710
+ }
600
711
 
601
712
  /* call-seq:
602
713
  * client.abandon_results!
@@ -632,80 +743,53 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
632
743
  * client.query(sql, options = {})
633
744
  *
634
745
  * Query the database with +sql+, with optional +options+. For the possible
635
- * options, see @@default_query_options on the Mysql2::Client class.
746
+ * options, see default_query_options on the Mysql2::Client class.
636
747
  */
637
- static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
748
+ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
638
749
  #ifndef _WIN32
639
750
  struct async_query_args async_args;
640
751
  #endif
641
752
  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
753
  GET_CLIENT(self);
649
754
 
650
755
  REQUIRE_CONNECTED(wrapper);
651
756
  args.mysql = wrapper->client;
652
757
 
653
- current = rb_hash_dup(rb_iv_get(self, "@query_options"));
654
- RB_GC_GUARD(current);
758
+ (void)RB_GC_GUARD(current);
655
759
  Check_Type(current, T_HASH);
656
760
  rb_iv_set(self, "@current_query_options", current);
657
761
 
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);
762
+ Check_Type(sql, T_STRING);
667
763
  #ifdef HAVE_RUBY_ENCODING_H
668
- conn_enc = rb_to_encoding(wrapper->encoding);
669
764
  /* ensure the string is in the encoding the connection is expecting */
670
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
765
+ args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
766
+ #else
767
+ args.sql = sql;
671
768
  #endif
672
- args.sql_ptr = StringValuePtr(args.sql);
769
+ args.sql_ptr = RSTRING_PTR(args.sql);
673
770
  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
771
  args.wrapper = wrapper;
690
772
 
773
+ rb_mysql_client_set_active_thread(self);
774
+
691
775
  #ifndef _WIN32
692
776
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
693
777
 
694
- if (!async) {
778
+ if (rb_hash_aref(current, sym_async) == Qtrue) {
779
+ return Qnil;
780
+ } else {
695
781
  async_args.fd = wrapper->client->net.fd;
696
782
  async_args.self = self;
697
783
 
698
784
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
699
785
 
700
- return rb_mysql_client_async_result(self);
701
- } else {
702
- return Qnil;
786
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
703
787
  }
704
788
  #else
705
789
  do_send_query(&args);
706
790
 
707
791
  /* this will just block until the result is ready */
708
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
792
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
709
793
  #endif
710
794
  }
711
795
 
@@ -736,9 +820,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
736
820
  oldLen = RSTRING_LEN(str);
737
821
  newStr = xmalloc(oldLen*2+1);
738
822
 
739
- newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
823
+ newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
740
824
  if (newLen == oldLen) {
741
825
  /* no need to return a new ruby string if nothing changed */
826
+ #ifdef HAVE_RUBY_ENCODING_H
827
+ if (default_internal_enc) {
828
+ str = rb_str_export_to_enc(str, default_internal_enc);
829
+ }
830
+ #endif
742
831
  xfree(newStr);
743
832
  return str;
744
833
  } else {
@@ -759,7 +848,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
759
848
  const void *retval = NULL;
760
849
  unsigned int intval = 0;
761
850
  const char * charval = NULL;
762
- my_bool boolval;
851
+ bool boolval;
763
852
 
764
853
  GET_CLIENT(self);
765
854
 
@@ -800,20 +889,27 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
800
889
  break;
801
890
 
802
891
  case MYSQL_READ_DEFAULT_FILE:
803
- charval = (const char *)StringValuePtr(value);
892
+ charval = (const char *)StringValueCStr(value);
804
893
  retval = charval;
805
894
  break;
806
895
 
807
896
  case MYSQL_READ_DEFAULT_GROUP:
808
- charval = (const char *)StringValuePtr(value);
897
+ charval = (const char *)StringValueCStr(value);
809
898
  retval = charval;
810
899
  break;
811
900
 
812
901
  case MYSQL_INIT_COMMAND:
813
- charval = (const char *)StringValuePtr(value);
902
+ charval = (const char *)StringValueCStr(value);
814
903
  retval = charval;
815
904
  break;
816
905
 
906
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
907
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
908
+ boolval = (value == Qfalse ? 0 : 1);
909
+ retval = &boolval;
910
+ break;
911
+ #endif
912
+
817
913
  default:
818
914
  return Qfalse;
819
915
  }
@@ -843,30 +939,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
843
939
  *
844
940
  * Returns a string that represents the client library version.
845
941
  */
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();
942
+ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
943
+ VALUE version_info, version, header_version;
944
+ version_info = rb_hash_new();
854
945
 
855
- #ifdef HAVE_RUBY_ENCODING_H
856
- default_internal_enc = rb_default_internal_encoding();
857
- conn_enc = rb_to_encoding(wrapper->encoding);
858
- #endif
946
+ version = rb_str_new2(mysql_get_client_info());
947
+ header_version = rb_str_new2(MYSQL_LINK_VERSION);
859
948
 
860
- rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
861
- client_info = rb_str_new2(mysql_get_client_info());
862
949
  #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
- }
950
+ rb_enc_associate(version, rb_usascii_encoding());
951
+ rb_enc_associate(header_version, rb_usascii_encoding());
867
952
  #endif
868
- rb_hash_aset(version, sym_version, client_info);
869
- return version;
953
+
954
+ rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
955
+ rb_hash_aset(version_info, sym_version, version);
956
+ rb_hash_aset(version_info, sym_header_version, header_version);
957
+
958
+ return version_info;
870
959
  }
871
960
 
872
961
  /* call-seq:
@@ -906,19 +995,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
906
995
  *
907
996
  * Return the file descriptor number for this client.
908
997
  */
998
+ #ifndef _WIN32
909
999
  static VALUE rb_mysql_client_socket(VALUE self) {
910
1000
  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
- }
1001
+ REQUIRE_CONNECTED(wrapper);
1002
+ return INT2NUM(wrapper->client->net.fd);
1003
+ }
918
1004
  #else
1005
+ static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
919
1006
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
920
- #endif
921
1007
  }
1008
+ #endif
922
1009
 
923
1010
  /* call-seq:
924
1011
  * client.last_id
@@ -987,7 +1074,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
987
1074
  REQUIRE_CONNECTED(wrapper);
988
1075
 
989
1076
  args.mysql = wrapper->client;
990
- args.db = StringValuePtr(db);
1077
+ args.db = StringValueCStr(db);
991
1078
 
992
1079
  if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
993
1080
  rb_raise_mysql2_error(wrapper);
@@ -1012,7 +1099,7 @@ static void *nogvl_ping(void *ptr) {
1012
1099
  static VALUE rb_mysql_client_ping(VALUE self) {
1013
1100
  GET_CLIENT(self);
1014
1101
 
1015
- if (!wrapper->connected) {
1102
+ if (!CONNECTED(wrapper)) {
1016
1103
  return Qfalse;
1017
1104
  } else {
1018
1105
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
@@ -1027,10 +1114,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1027
1114
  static VALUE rb_mysql_client_more_results(VALUE self)
1028
1115
  {
1029
1116
  GET_CLIENT(self);
1030
- if (mysql_more_results(wrapper->client) == 0)
1031
- return Qfalse;
1032
- else
1033
- return Qtrue;
1117
+ if (mysql_more_results(wrapper->client) == 0)
1118
+ return Qfalse;
1119
+ else
1120
+ return Qtrue;
1034
1121
  }
1035
1122
 
1036
1123
  /* call-seq:
@@ -1078,9 +1165,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1078
1165
  }
1079
1166
 
1080
1167
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1081
- RB_GC_GUARD(current);
1168
+ (void)RB_GC_GUARD(current);
1082
1169
  Check_Type(current, T_HASH);
1083
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1170
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
1084
1171
 
1085
1172
  return resultObj;
1086
1173
  }
@@ -1097,6 +1184,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1097
1184
  }
1098
1185
  #endif
1099
1186
 
1187
+ /* call-seq:
1188
+ * client.automatic_close?
1189
+ *
1190
+ * @return [Boolean]
1191
+ */
1192
+ static VALUE get_automatic_close(VALUE self) {
1193
+ GET_CLIENT(self);
1194
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1195
+ }
1196
+
1197
+ /* call-seq:
1198
+ * client.automatic_close = false
1199
+ *
1200
+ * Set this to +false+ to leave the connection open after it is garbage
1201
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1202
+ * call +close+ when the connection is no longer needed.
1203
+ *
1204
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1205
+ */
1206
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1207
+ GET_CLIENT(self);
1208
+ if (RTEST(value)) {
1209
+ wrapper->automatic_close = 1;
1210
+ } else {
1211
+ #ifndef _WIN32
1212
+ wrapper->automatic_close = 0;
1213
+ #else
1214
+ rb_warn("Connections are always closed by garbage collector on Windows");
1215
+ #endif
1216
+ }
1217
+ return value;
1218
+ }
1219
+
1100
1220
  /* call-seq:
1101
1221
  * client.reconnect = true
1102
1222
  *
@@ -1149,18 +1269,17 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1149
1269
  static VALUE set_charset_name(VALUE self, VALUE value) {
1150
1270
  char *charset_name;
1151
1271
  #ifdef HAVE_RUBY_ENCODING_H
1152
- size_t charset_name_len;
1153
1272
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1154
1273
  rb_encoding *enc;
1155
1274
  VALUE rb_enc;
1156
1275
  #endif
1157
1276
  GET_CLIENT(self);
1158
1277
 
1278
+ Check_Type(value, T_STRING);
1159
1279
  charset_name = RSTRING_PTR(value);
1160
1280
 
1161
1281
  #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);
1282
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1164
1283
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1165
1284
  VALUE inspect = rb_inspect(value);
1166
1285
  rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
@@ -1183,11 +1302,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1183
1302
  GET_CLIENT(self);
1184
1303
 
1185
1304
  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));
1305
+ NIL_P(key) ? NULL : StringValueCStr(key),
1306
+ NIL_P(cert) ? NULL : StringValueCStr(cert),
1307
+ NIL_P(ca) ? NULL : StringValueCStr(ca),
1308
+ NIL_P(capath) ? NULL : StringValueCStr(capath),
1309
+ NIL_P(cipher) ? NULL : StringValueCStr(cipher));
1191
1310
 
1192
1311
  return self;
1193
1312
  }
@@ -1208,19 +1327,39 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1208
1327
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1209
1328
  }
1210
1329
 
1330
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1331
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1332
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1333
+ #else
1334
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1335
+ #endif
1336
+ }
1337
+
1211
1338
  static VALUE initialize_ext(VALUE self) {
1212
1339
  GET_CLIENT(self);
1213
1340
 
1214
1341
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1215
1342
  /* TODO: warning - not enough memory? */
1216
- return rb_raise_mysql2_error(wrapper);
1343
+ rb_raise_mysql2_error(wrapper);
1217
1344
  }
1218
1345
 
1219
1346
  wrapper->initialized = 1;
1220
1347
  return self;
1221
1348
  }
1222
1349
 
1350
+ /* call-seq: client.prepare # => Mysql2::Statement
1351
+ *
1352
+ * Create a new prepared statement.
1353
+ */
1354
+ static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1355
+ GET_CLIENT(self);
1356
+ REQUIRE_CONNECTED(wrapper);
1357
+
1358
+ return rb_mysql_stmt_new(self, sql);
1359
+ }
1360
+
1223
1361
  void init_mysql2_client() {
1362
+ #ifdef _WIN32
1224
1363
  /* verify the libmysql we're about to use was the version we were built against
1225
1364
  https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
1226
1365
  int i;
@@ -1235,15 +1374,14 @@ void init_mysql2_client() {
1235
1374
  }
1236
1375
  if (lib[i] != MYSQL_LINK_VERSION[i]) {
1237
1376
  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
1377
  }
1240
1378
  }
1379
+ #endif
1241
1380
 
1242
1381
  /* Initializing mysql library, so different threads could call Client.new */
1243
1382
  /* without race condition in the library */
1244
1383
  if (mysql_library_init(0, NULL, NULL) != 0) {
1245
1384
  rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1246
- return;
1247
1385
  }
1248
1386
 
1249
1387
  #if 0
@@ -1254,26 +1392,30 @@ void init_mysql2_client() {
1254
1392
  rb_define_alloc_func(cMysql2Client, allocate);
1255
1393
 
1256
1394
  rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
1395
+ rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1257
1396
 
1258
1397
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1259
- rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1398
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1260
1399
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1261
1400
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1262
- rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1263
1401
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
1264
1402
  rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
1265
1403
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
1266
1404
  rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
1267
1405
  rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1406
+ rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
1268
1407
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1269
1408
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1270
1409
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1271
1410
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1272
1411
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1273
1412
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1413
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1414
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1274
1415
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1275
1416
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1276
1417
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1418
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1277
1419
  #ifdef HAVE_RUBY_ENCODING_H
1278
1420
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1279
1421
  #endif
@@ -1288,25 +1430,33 @@ void init_mysql2_client() {
1288
1430
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1289
1431
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1290
1432
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1433
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1434
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1291
1435
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1292
1436
  rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1437
+ rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
1293
1438
 
1294
1439
  sym_id = ID2SYM(rb_intern("id"));
1295
1440
  sym_version = ID2SYM(rb_intern("version"));
1441
+ sym_header_version = ID2SYM(rb_intern("header_version"));
1296
1442
  sym_async = ID2SYM(rb_intern("async"));
1297
1443
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1298
1444
  sym_as = ID2SYM(rb_intern("as"));
1299
1445
  sym_array = ID2SYM(rb_intern("array"));
1300
1446
  sym_stream = ID2SYM(rb_intern("stream"));
1301
1447
 
1448
+ intern_brackets = rb_intern("[]");
1302
1449
  intern_merge = rb_intern("merge");
1303
1450
  intern_merge_bang = rb_intern("merge!");
1304
- intern_error_number_eql = rb_intern("error_number=");
1305
- intern_sql_state_eql = rb_intern("sql_state=");
1451
+ intern_new_with_args = rb_intern("new_with_args");
1306
1452
 
1307
1453
  #ifdef CLIENT_LONG_PASSWORD
1308
1454
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1309
1455
  LONG2NUM(CLIENT_LONG_PASSWORD));
1456
+ #else
1457
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1458
+ * but we're using it in our default connection flags. */
1459
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1310
1460
  #endif
1311
1461
 
1312
1462
  #ifdef CLIENT_FOUND_ROWS
@@ -1413,4 +1563,31 @@ void init_mysql2_client() {
1413
1563
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1414
1564
  LONG2NUM(CLIENT_BASIC_FLAGS));
1415
1565
  #endif
1566
+
1567
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1568
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1569
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1570
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1571
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1572
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1573
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1574
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1575
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1576
+ #endif
1577
+
1578
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1579
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1580
+ #endif
1581
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1582
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1583
+ #endif
1584
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1585
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1586
+ #endif
1587
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1588
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1589
+ #endif
1590
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1591
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1592
+ #endif
1416
1593
  }