mysql2 0.4.2 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
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