mysql2 0.3.18 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }