mysql2 0.4.2 → 0.4.10

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.
data/ext/mysql2/client.c CHANGED
@@ -30,9 +30,21 @@ 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
+
39
+ #define REQUIRE_CONNECTED(wrapper) \
40
+ REQUIRE_INITIALIZED(wrapper) \
41
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
42
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
43
+ }
44
+
33
45
  #define REQUIRE_NOT_CONNECTED(wrapper) \
34
46
  REQUIRE_INITIALIZED(wrapper) \
35
- if (wrapper->connected) { \
47
+ if (CONNECTED(wrapper)) { \
36
48
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
37
49
  }
38
50
 
@@ -41,12 +53,24 @@ VALUE rb_hash_dup(VALUE other) {
41
53
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
42
54
  * linking against the server itself
43
55
  */
44
- #ifdef LIBMYSQL_VERSION
56
+ #if defined(MARIADB_CLIENT_VERSION_STR)
57
+ #define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
58
+ #elif defined(LIBMYSQL_VERSION)
45
59
  #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
46
60
  #else
47
61
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
48
62
  #endif
49
63
 
64
+ /*
65
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
66
+ */
67
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
68
+ #define SSL_MODE_DISABLED 1
69
+ #define SSL_MODE_REQUIRED 3
70
+ #define HAVE_CONST_SSL_MODE_DISABLED
71
+ #define HAVE_CONST_SSL_MODE_REQUIRED
72
+ #endif
73
+
50
74
  /*
51
75
  * used to pass all arguments to mysql_real_connect while inside
52
76
  * rb_thread_call_without_gvl
@@ -83,6 +107,42 @@ struct nogvl_select_db_args {
83
107
  char *db;
84
108
  };
85
109
 
110
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
111
+ unsigned long version = mysql_get_client_version();
112
+
113
+ if (version < 50703) {
114
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
115
+ return Qnil;
116
+ }
117
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
118
+ GET_CLIENT(self);
119
+ int val = NUM2INT( setting );
120
+ if (version >= 50703 && version < 50711) {
121
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
122
+ bool b = ( val == SSL_MODE_REQUIRED );
123
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
124
+ return INT2NUM(result);
125
+ } else {
126
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
127
+ return Qnil;
128
+ }
129
+ }
130
+ #endif
131
+ #ifdef FULL_SSL_MODE_SUPPORT
132
+ GET_CLIENT(self);
133
+ int val = NUM2INT( setting );
134
+
135
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
136
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
137
+ }
138
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
139
+
140
+ return INT2NUM(result);
141
+ #endif
142
+ #ifdef NO_SSL_MODE_SUPPORT
143
+ return Qnil;
144
+ #endif
145
+ }
86
146
  /*
87
147
  * non-blocking mysql_*() functions that we won't be wrapping since
88
148
  * they do not appear to hit the network nor issue any interruptible
@@ -211,11 +271,10 @@ static VALUE invalidate_fd(int clientfd)
211
271
  static void *nogvl_close(void *ptr) {
212
272
  mysql_client_wrapper *wrapper = ptr;
213
273
 
214
- if (wrapper->client) {
274
+ if (!wrapper->closed) {
215
275
  mysql_close(wrapper->client);
216
- xfree(wrapper->client);
217
- wrapper->client = NULL;
218
- wrapper->connected = 0;
276
+ wrapper->closed = 1;
277
+ wrapper->reconnect_enabled = 0;
219
278
  wrapper->active_thread = Qnil;
220
279
  }
221
280
 
@@ -234,7 +293,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
234
293
 
235
294
  if (wrapper->refcount == 0) {
236
295
  #ifndef _WIN32
237
- if (wrapper->connected) {
296
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
238
297
  /* The client is being garbage collected while connected. Prevent
239
298
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
240
299
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -246,10 +305,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
246
305
  fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
247
306
  close(wrapper->client->net.fd);
248
307
  }
308
+ wrapper->client->net.fd = -1;
249
309
  }
250
310
  #endif
251
311
 
252
312
  nogvl_close(wrapper);
313
+ xfree(wrapper->client);
253
314
  xfree(wrapper);
254
315
  }
255
316
  }
@@ -259,13 +320,14 @@ static VALUE allocate(VALUE klass) {
259
320
  mysql_client_wrapper * wrapper;
260
321
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
322
  wrapper->encoding = Qnil;
262
- MARK_CONN_INACTIVE(self);
323
+ wrapper->active_thread = Qnil;
324
+ wrapper->automatic_close = 1;
263
325
  wrapper->server_version = 0;
264
326
  wrapper->reconnect_enabled = 0;
265
327
  wrapper->connect_timeout = 0;
266
- wrapper->connected = 0; /* means that a database connection is open */
267
328
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
268
329
  wrapper->refcount = 1;
330
+ wrapper->closed = 0;
269
331
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
270
332
 
271
333
  return obj;
@@ -331,6 +393,26 @@ static VALUE rb_mysql_info(VALUE self) {
331
393
  return rb_str;
332
394
  }
333
395
 
396
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
397
+ {
398
+ const char *cipher;
399
+ VALUE rb_str;
400
+ GET_CLIENT(self);
401
+
402
+ cipher = mysql_get_ssl_cipher(wrapper->client);
403
+
404
+ if (cipher == NULL) {
405
+ return Qnil;
406
+ }
407
+
408
+ rb_str = rb_str_new2(cipher);
409
+ #ifdef HAVE_RUBY_ENCODING_H
410
+ rb_enc_associate(rb_str, rb_utf8_encoding());
411
+ #endif
412
+
413
+ return rb_str;
414
+ }
415
+
334
416
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
335
417
  struct nogvl_connect_args args;
336
418
  time_t start_time, end_time, elapsed_time, connect_timeout;
@@ -372,33 +454,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
454
  if (wrapper->connect_timeout)
373
455
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
456
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
457
+ rb_raise_mysql2_error(wrapper);
376
458
  }
377
459
 
378
460
  wrapper->server_version = mysql_get_server_version(wrapper->client);
379
- wrapper->connected = 1;
380
461
  return self;
381
462
  }
382
463
 
383
464
  /*
384
- * Terminate the connection; call this when the connection is no longer needed.
385
- * The garbage collector can close the connection, but doing so emits an
386
- * "Aborted connection" error on the server and increments the Aborted_clients
387
- * status variable.
465
+ * Immediately disconnect from the server; normally the garbage collector
466
+ * will disconnect automatically when a connection is no longer needed.
467
+ * Explicitly closing this will free up server resources sooner than waiting
468
+ * for the garbage collector.
388
469
  *
389
- * @see http://dev.mysql.com/doc/en/communication-errors.html
390
- * @return [void]
470
+ * @return [nil]
391
471
  */
392
472
  static VALUE rb_mysql_client_close(VALUE self) {
393
473
  GET_CLIENT(self);
394
474
 
395
- if (wrapper->connected) {
475
+ if (wrapper->client) {
396
476
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
397
477
  }
398
478
 
399
479
  return Qnil;
400
480
  }
401
481
 
482
+ /* call-seq:
483
+ * client.closed?
484
+ *
485
+ * @return [Boolean]
486
+ */
487
+ static VALUE rb_mysql_client_closed(VALUE self) {
488
+ GET_CLIENT(self);
489
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
490
+ }
491
+
402
492
  /*
403
493
  * mysql_send_query is unlikely to block since most queries are small
404
494
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -418,8 +508,8 @@ static VALUE do_send_query(void *args) {
418
508
  mysql_client_wrapper *wrapper = query_args->wrapper;
419
509
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
420
510
  /* an error occurred, we're not active anymore */
421
- MARK_CONN_INACTIVE(self);
422
- return rb_raise_mysql2_error(wrapper);
511
+ wrapper->active_thread = Qnil;
512
+ rb_raise_mysql2_error(wrapper);
423
513
  }
424
514
  return Qnil;
425
515
  }
@@ -431,7 +521,7 @@ static VALUE do_send_query(void *args) {
431
521
  */
432
522
  static void *nogvl_read_query_result(void *ptr) {
433
523
  MYSQL * client = ptr;
434
- my_bool res = mysql_read_query_result(client);
524
+ bool res = mysql_read_query_result(client);
435
525
 
436
526
  return (void *)(res == 0 ? Qtrue : Qfalse);
437
527
  }
@@ -448,7 +538,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
448
538
 
449
539
  /* once our result is stored off, this connection is
450
540
  ready for another command to be issued */
451
- MARK_CONN_INACTIVE(self);
541
+ wrapper->active_thread = Qnil;
452
542
 
453
543
  return result;
454
544
  }
@@ -480,8 +570,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
480
570
  REQUIRE_CONNECTED(wrapper);
481
571
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
482
572
  /* an error occurred, mark this connection inactive */
483
- MARK_CONN_INACTIVE(self);
484
- return rb_raise_mysql2_error(wrapper);
573
+ wrapper->active_thread = Qnil;
574
+ rb_raise_mysql2_error(wrapper);
485
575
  }
486
576
 
487
577
  is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
@@ -493,7 +583,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
493
583
 
494
584
  if (result == NULL) {
495
585
  if (mysql_errno(wrapper->client) != 0) {
496
- MARK_CONN_INACTIVE(self);
586
+ wrapper->active_thread = Qnil;
497
587
  rb_raise_mysql2_error(wrapper);
498
588
  }
499
589
  /* no data and no error, so query was not a SELECT */
@@ -517,15 +607,17 @@ struct async_query_args {
517
607
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
518
608
  GET_CLIENT(self);
519
609
 
520
- MARK_CONN_INACTIVE(self);
521
- wrapper->connected = 0;
610
+ wrapper->active_thread = Qnil;
522
611
 
523
612
  /* Invalidate the MySQL socket to prevent further communication.
524
613
  * The GC will come along later and call mysql_close to free it.
525
614
  */
526
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
527
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
528
- close(wrapper->client->net.fd);
615
+ if (CONNECTED(wrapper)) {
616
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
617
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
618
+ close(wrapper->client->net.fd);
619
+ }
620
+ wrapper->client->net.fd = -1;
529
621
  }
530
622
 
531
623
  rb_exc_raise(error);
@@ -574,26 +666,32 @@ static VALUE do_query(void *args) {
574
666
 
575
667
  return Qnil;
576
668
  }
577
- #else
578
- static VALUE finish_and_mark_inactive(void *args) {
579
- VALUE self = args;
580
- MYSQL_RES *result;
669
+ #endif
581
670
 
671
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
582
672
  GET_CLIENT(self);
583
673
 
674
+ /* Check if execution terminated while result was still being read. */
584
675
  if (!NIL_P(wrapper->active_thread)) {
585
- /* if we got here, the result hasn't been read off the wire yet
586
- so lets do that and then throw it away because we have no way
587
- of getting it back up to the caller from here */
588
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
589
- mysql_free_result(result);
590
-
591
- MARK_CONN_INACTIVE(self);
676
+ if (CONNECTED(wrapper)) {
677
+ /* Invalidate the MySQL socket to prevent further communication. */
678
+ #ifndef _WIN32
679
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
680
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
681
+ close(wrapper->client->net.fd);
682
+ }
683
+ #else
684
+ close(wrapper->client->net.fd);
685
+ #endif
686
+ wrapper->client->net.fd = -1;
687
+ }
688
+ /* Skip mysql client check performed before command execution. */
689
+ wrapper->client->status = MYSQL_STATUS_READY;
690
+ wrapper->active_thread = Qnil;
592
691
  }
593
692
 
594
693
  return Qnil;
595
694
  }
596
- #endif
597
695
 
598
696
  void rb_mysql_client_set_active_thread(VALUE self) {
599
697
  VALUE thread_current = rb_thread_current();
@@ -687,13 +785,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
687
785
 
688
786
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
689
787
 
690
- return rb_mysql_client_async_result(self);
788
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
691
789
  }
692
790
  #else
693
791
  do_send_query(&args);
694
792
 
695
793
  /* this will just block until the result is ready */
696
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
794
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
697
795
  #endif
698
796
  }
699
797
 
@@ -752,7 +850,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
752
850
  const void *retval = NULL;
753
851
  unsigned int intval = 0;
754
852
  const char * charval = NULL;
755
- my_bool boolval;
853
+ bool boolval;
756
854
 
757
855
  GET_CLIENT(self);
758
856
 
@@ -787,10 +885,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
787
885
  retval = &boolval;
788
886
  break;
789
887
 
888
+ #ifdef MYSQL_SECURE_AUTH
790
889
  case MYSQL_SECURE_AUTH:
791
890
  boolval = (value == Qfalse ? 0 : 1);
792
891
  retval = &boolval;
793
892
  break;
893
+ #endif
794
894
 
795
895
  case MYSQL_READ_DEFAULT_FILE:
796
896
  charval = (const char *)StringValueCStr(value);
@@ -807,6 +907,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
807
907
  retval = charval;
808
908
  break;
809
909
 
910
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
911
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
912
+ boolval = (value == Qfalse ? 0 : 1);
913
+ retval = &boolval;
914
+ break;
915
+ #endif
916
+
810
917
  default:
811
918
  return Qfalse;
812
919
  }
@@ -996,7 +1103,7 @@ static void *nogvl_ping(void *ptr) {
996
1103
  static VALUE rb_mysql_client_ping(VALUE self) {
997
1104
  GET_CLIENT(self);
998
1105
 
999
- if (!wrapper->connected) {
1106
+ if (!CONNECTED(wrapper)) {
1000
1107
  return Qfalse;
1001
1108
  } else {
1002
1109
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
@@ -1011,10 +1118,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1011
1118
  static VALUE rb_mysql_client_more_results(VALUE self)
1012
1119
  {
1013
1120
  GET_CLIENT(self);
1014
- if (mysql_more_results(wrapper->client) == 0)
1015
- return Qfalse;
1016
- else
1017
- return Qtrue;
1121
+ if (mysql_more_results(wrapper->client) == 0)
1122
+ return Qfalse;
1123
+ else
1124
+ return Qtrue;
1018
1125
  }
1019
1126
 
1020
1127
  /* call-seq:
@@ -1081,6 +1188,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1081
1188
  }
1082
1189
  #endif
1083
1190
 
1191
+ /* call-seq:
1192
+ * client.automatic_close?
1193
+ *
1194
+ * @return [Boolean]
1195
+ */
1196
+ static VALUE get_automatic_close(VALUE self) {
1197
+ GET_CLIENT(self);
1198
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1199
+ }
1200
+
1201
+ /* call-seq:
1202
+ * client.automatic_close = false
1203
+ *
1204
+ * Set this to +false+ to leave the connection open after it is garbage
1205
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1206
+ * call +close+ when the connection is no longer needed.
1207
+ *
1208
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1209
+ */
1210
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1211
+ GET_CLIENT(self);
1212
+ if (RTEST(value)) {
1213
+ wrapper->automatic_close = 1;
1214
+ } else {
1215
+ #ifndef _WIN32
1216
+ wrapper->automatic_close = 0;
1217
+ #else
1218
+ rb_warn("Connections are always closed by garbage collector on Windows");
1219
+ #endif
1220
+ }
1221
+ return value;
1222
+ }
1223
+
1084
1224
  /* call-seq:
1085
1225
  * client.reconnect = true
1086
1226
  *
@@ -1139,6 +1279,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1139
1279
  #endif
1140
1280
  GET_CLIENT(self);
1141
1281
 
1282
+ Check_Type(value, T_STRING);
1142
1283
  charset_name = RSTRING_PTR(value);
1143
1284
 
1144
1285
  #ifdef HAVE_RUBY_ENCODING_H
@@ -1175,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1175
1316
  }
1176
1317
 
1177
1318
  static VALUE set_secure_auth(VALUE self, VALUE value) {
1319
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1320
+ #ifdef MYSQL_SECURE_AUTH
1178
1321
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1322
+ #else
1323
+ return Qfalse;
1324
+ #endif
1179
1325
  }
1180
1326
 
1181
1327
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1190,12 +1336,20 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1190
1336
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1191
1337
  }
1192
1338
 
1339
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1342
+ #else
1343
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1344
+ #endif
1345
+ }
1346
+
1193
1347
  static VALUE initialize_ext(VALUE self) {
1194
1348
  GET_CLIENT(self);
1195
1349
 
1196
1350
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1197
1351
  /* TODO: warning - not enough memory? */
1198
- return rb_raise_mysql2_error(wrapper);
1352
+ rb_raise_mysql2_error(wrapper);
1199
1353
  }
1200
1354
 
1201
1355
  wrapper->initialized = 1;
@@ -1250,6 +1404,7 @@ void init_mysql2_client() {
1250
1404
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1251
1405
 
1252
1406
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1407
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1253
1408
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1254
1409
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1255
1410
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1264,9 +1419,12 @@ void init_mysql2_client() {
1264
1419
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1265
1420
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1266
1421
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1422
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1423
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1267
1424
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1268
1425
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1269
1426
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1427
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1270
1428
  #ifdef HAVE_RUBY_ENCODING_H
1271
1429
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1272
1430
  #endif
@@ -1281,6 +1439,8 @@ void init_mysql2_client() {
1281
1439
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1282
1440
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1283
1441
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1442
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1443
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1284
1444
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1285
1445
  rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1286
1446
  rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
@@ -1302,6 +1462,10 @@ void init_mysql2_client() {
1302
1462
  #ifdef CLIENT_LONG_PASSWORD
1303
1463
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1304
1464
  LONG2NUM(CLIENT_LONG_PASSWORD));
1465
+ #else
1466
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1467
+ * but we're using it in our default connection flags. */
1468
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1305
1469
  #endif
1306
1470
 
1307
1471
  #ifdef CLIENT_FOUND_ROWS
@@ -1408,4 +1572,31 @@ void init_mysql2_client() {
1408
1572
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1409
1573
  LONG2NUM(CLIENT_BASIC_FLAGS));
1410
1574
  #endif
1575
+
1576
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1577
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1578
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1579
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1580
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1581
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1582
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1583
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1584
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1585
+ #endif
1586
+
1587
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1588
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1589
+ #endif
1590
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1591
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1592
+ #endif
1593
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1594
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1595
+ #endif
1596
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1597
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1598
+ #endif
1599
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1600
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1601
+ #endif
1411
1602
  }
data/ext/mysql2/client.h CHANGED
@@ -43,25 +43,15 @@ typedef struct {
43
43
  int reconnect_enabled;
44
44
  unsigned int connect_timeout;
45
45
  int active;
46
- int connected;
46
+ int automatic_close;
47
47
  int initialized;
48
48
  int refcount;
49
- int freed;
49
+ int closed;
50
50
  MYSQL *client;
51
51
  } mysql_client_wrapper;
52
52
 
53
- #define REQUIRE_CONNECTED(wrapper) \
54
- REQUIRE_INITIALIZED(wrapper) \
55
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
56
- rb_raise(cMysql2Error, "closed MySQL connection"); \
57
- }
58
-
59
53
  void rb_mysql_client_set_active_thread(VALUE self);
60
54
 
61
- #define MARK_CONN_INACTIVE(conn) do {\
62
- wrapper->active_thread = Qnil; \
63
- } while(0)
64
-
65
55
  #define GET_CLIENT(self) \
66
56
  mysql_client_wrapper *wrapper; \
67
57
  Data_Get_Struct(self, mysql_client_wrapper, wrapper);
@@ -12,6 +12,20 @@ def asplode(lib)
12
12
  end
13
13
  end
14
14
 
15
+ def add_ssl_defines(header)
16
+ all_modes_found = %w(SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY).inject(true) do |m, ssl_mode|
17
+ m && have_const(ssl_mode, header)
18
+ end
19
+ $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
20
+ # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
21
+ has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
22
+ $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
23
+ end
24
+
25
+ # 2.1+
26
+ have_func('rb_absint_size')
27
+ have_func('rb_absint_singlebit_p')
28
+
15
29
  # 2.0-only
16
30
  have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
17
31
 
@@ -20,6 +34,7 @@ have_func('rb_thread_blocking_region')
20
34
  have_func('rb_wait_for_single_fd')
21
35
  have_func('rb_hash_dup')
22
36
  have_func('rb_intern3')
37
+ have_func('rb_big_cmp')
23
38
 
24
39
  # borrowed from mysqlplus
25
40
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
@@ -37,6 +52,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
37
52
  /usr/local/opt/mysql5*
38
53
  ).map { |dir| dir << '/bin' }
39
54
 
55
+ # For those without HOMEBREW_ROOT in PATH
56
+ dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
57
+
40
58
  GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
41
59
 
42
60
  # If the user has provided a --with-mysql-dir argument, we must respect it or fail.
@@ -87,11 +105,17 @@ else
87
105
  asplode 'mysql.h'
88
106
  end
89
107
 
90
- %w(errmsg.h mysqld_error.h).each do |h|
91
- header = [prefix, h].compact.join '/'
108
+ %w(errmsg.h).each do |h|
109
+ header = [prefix, h].compact.join('/')
92
110
  asplode h unless have_header header
93
111
  end
94
112
 
113
+ mysql_h = [prefix, 'mysql.h'].compact.join('/')
114
+ add_ssl_defines(mysql_h)
115
+ have_struct_member('MYSQL', 'net.vio', mysql_h)
116
+ have_struct_member('MYSQL', 'net.pvio', mysql_h)
117
+ have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
118
+
95
119
  # This is our wishlist. We use whichever flags work on the host.
96
120
  # -Wall and -Wextra are included by default.
97
121
  wishlist = [
@@ -11,14 +11,10 @@ void Init_mysql2(void);
11
11
 
12
12
  #ifdef HAVE_MYSQL_H
13
13
  #include <mysql.h>
14
- #include <mysql_com.h>
15
14
  #include <errmsg.h>
16
- #include <mysqld_error.h>
17
15
  #else
18
16
  #include <mysql/mysql.h>
19
- #include <mysql/mysql_com.h>
20
17
  #include <mysql/errmsg.h>
21
- #include <mysql/mysqld_error.h>
22
18
  #endif
23
19
 
24
20
  #ifdef HAVE_RUBY_ENCODING_H