mysql2 0.5.3 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mysql2/client.c CHANGED
@@ -45,7 +45,7 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
45
45
  }
46
46
 
47
47
  /*
48
- * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
49
49
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
50
50
  * linking against the server itself
51
51
  */
@@ -58,8 +58,23 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
58
58
  #endif
59
59
 
60
60
  /*
61
- * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
61
+ * mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
62
+ * while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
63
+ * This is a hack to take care of both clients.
62
64
  */
65
+ #if defined(CLIENT_SESSION_TRACK)
66
+ #elif defined(CLIENT_SESSION_TRACKING)
67
+ #define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
68
+ #define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
69
+ #endif
70
+
71
+ /*
72
+ * compatibility with mysql-connector-c 6.1.x, MySQL 5.7.3 - 5.7.10 & with MariaDB 10.x and later.
73
+ */
74
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
75
+ #define SSL_MODE_VERIFY_IDENTITY 5
76
+ #define HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
77
+ #endif
63
78
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
64
79
  #define SSL_MODE_DISABLED 1
65
80
  #define SSL_MODE_REQUIRED 3
@@ -105,44 +120,80 @@ struct nogvl_select_db_args {
105
120
 
106
121
  static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
107
122
  unsigned long version = mysql_get_client_version();
123
+ const char *version_str = mysql_get_client_info();
108
124
 
109
- if (version < 50703) {
110
- rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
125
+ /* Warn about versions that are known to be incomplete; these are pretty
126
+ * ancient, we want people to upgrade if they need SSL/TLS to work
127
+ *
128
+ * MySQL 5.x before 5.6.30 -- ssl_mode introduced but not fully working until 5.6.36)
129
+ * MySQL 5.7 before 5.7.3 -- ssl_mode introduced but not fully working until 5.7.11)
130
+ */
131
+ if ((version >= 50000 && version < 50630) || (version >= 50700 && version < 50703)) {
132
+ rb_warn("Your mysql client library version %s does not support setting ssl_mode; full support comes with 5.6.36+, 5.7.11+, 8.0+", version_str);
111
133
  return Qnil;
112
134
  }
113
- #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
135
+
136
+ /* For these versions, map from the options we're exposing to Ruby to the constant available:
137
+ * ssl_mode: :verify_identity to MYSQL_OPT_SSL_VERIFY_SERVER_CERT = 1
138
+ * ssl_mode: :required to MYSQL_OPT_SSL_ENFORCE = 1
139
+ * ssl_mode: :disabled to MYSQL_OPT_SSL_ENFORCE = 0
140
+ */
141
+ #if defined(HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT) || defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE)
114
142
  GET_CLIENT(self);
115
- int val = NUM2INT( setting );
116
- // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
117
- if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
143
+ int val = NUM2INT(setting);
144
+
145
+ /* Expected code path for MariaDB 10.x and MariaDB Connector/C 3.x
146
+ * Workaround code path for MySQL 5.7.3 - 5.7.10 and MySQL Connector/C 6.1.3 - 6.1.x
147
+ */
148
+ if (version >= 100000 // MariaDB (all versions numbered 10.x)
149
+ || (version >= 30000 && version < 40000) // MariaDB Connector/C (all versions numbered 3.x)
150
+ || (version >= 50703 && version < 50711) // Workaround for MySQL 5.7.3 - 5.7.10
151
+ || (version >= 60103 && version < 60200)) { // Workaround for MySQL Connector/C 6.1.3 - 6.1.x
152
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
153
+ if (val == SSL_MODE_VERIFY_IDENTITY) {
154
+ my_bool b = 1;
155
+ int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &b);
156
+ return INT2NUM(result);
157
+ }
158
+ #endif
159
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
118
160
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
119
- my_bool b = ( val == SSL_MODE_REQUIRED );
120
- int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
161
+ my_bool b = (val == SSL_MODE_REQUIRED);
162
+ int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b);
121
163
  return INT2NUM(result);
122
- } else {
123
- rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
124
- return Qnil;
125
164
  }
165
+ #endif
166
+ rb_warn("Your mysql client library version %s does not support ssl_mode %d", version_str, val);
167
+ return Qnil;
126
168
  } else {
127
- rb_warn( "Your mysql client library does not support ssl_mode as expected." );
169
+ rb_warn("Your mysql client library version %s does not support ssl_mode as expected", version_str);
128
170
  return Qnil;
129
171
  }
130
172
  #endif
173
+
174
+ /* For other versions -- known to be MySQL 5.6.36+, 5.7.11+, 8.0+
175
+ * pass the value of the argument to MYSQL_OPT_SSL_MODE -- note the code
176
+ * mapping from atoms / constants is in the MySQL::Client Ruby class
177
+ */
131
178
  #ifdef FULL_SSL_MODE_SUPPORT
132
179
  GET_CLIENT(self);
133
- int val = NUM2INT( setting );
180
+ int val = NUM2INT(setting);
134
181
 
135
182
  if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
136
183
  rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
137
184
  }
138
- int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
185
+ int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_MODE, &val);
139
186
 
140
187
  return INT2NUM(result);
141
188
  #endif
189
+
190
+ // Warn if we get this far
142
191
  #ifdef NO_SSL_MODE_SUPPORT
192
+ rb_warn("Your mysql client library does not support setting ssl_mode");
143
193
  return Qnil;
144
194
  #endif
145
195
  }
196
+
146
197
  /*
147
198
  * non-blocking mysql_*() functions that we won't be wrapping since
148
199
  * they do not appear to hit the network nor issue any interruptible
@@ -168,14 +219,50 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
168
219
  static void rb_mysql_client_mark(void * wrapper) {
169
220
  mysql_client_wrapper * w = wrapper;
170
221
  if (w) {
171
- rb_gc_mark(w->encoding);
172
- rb_gc_mark(w->active_thread);
222
+ rb_gc_mark_movable(w->encoding);
223
+ rb_gc_mark_movable(w->active_fiber);
224
+ }
225
+ }
226
+
227
+ /* this is called during GC */
228
+ static void rb_mysql_client_free(void *ptr) {
229
+ mysql_client_wrapper *wrapper = ptr;
230
+ decr_mysql2_client(wrapper);
231
+ }
232
+
233
+ static size_t rb_mysql_client_memsize(const void * wrapper) {
234
+ const mysql_client_wrapper * w = wrapper;
235
+ return sizeof(*w);
236
+ }
237
+
238
+ static void rb_mysql_client_compact(void * wrapper) {
239
+ mysql_client_wrapper * w = wrapper;
240
+ if (w) {
241
+ rb_mysql2_gc_location(w->encoding);
242
+ rb_mysql2_gc_location(w->active_fiber);
173
243
  }
174
244
  }
175
245
 
246
+ const rb_data_type_t rb_mysql_client_type = {
247
+ "rb_mysql_client",
248
+ {
249
+ rb_mysql_client_mark,
250
+ rb_mysql_client_free,
251
+ rb_mysql_client_memsize,
252
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
253
+ rb_mysql_client_compact,
254
+ #endif
255
+ },
256
+ 0,
257
+ 0,
258
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
259
+ RUBY_TYPED_FREE_IMMEDIATELY,
260
+ #endif
261
+ };
262
+
176
263
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
177
264
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
178
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
265
+ VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
179
266
  VALUE e;
180
267
 
181
268
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
@@ -269,22 +356,16 @@ static VALUE invalidate_fd(int clientfd)
269
356
  static void *nogvl_close(void *ptr) {
270
357
  mysql_client_wrapper *wrapper = ptr;
271
358
 
272
- if (!wrapper->closed) {
359
+ if (wrapper->initialized && !wrapper->closed) {
273
360
  mysql_close(wrapper->client);
274
361
  wrapper->closed = 1;
275
362
  wrapper->reconnect_enabled = 0;
276
- wrapper->active_thread = Qnil;
363
+ wrapper->active_fiber = Qnil;
277
364
  }
278
365
 
279
366
  return NULL;
280
367
  }
281
368
 
282
- /* this is called during GC */
283
- static void rb_mysql_client_free(void *ptr) {
284
- mysql_client_wrapper *wrapper = ptr;
285
- decr_mysql2_client(wrapper);
286
- }
287
-
288
369
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
289
370
  {
290
371
  wrapper->refcount--;
@@ -316,16 +397,20 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
316
397
  static VALUE allocate(VALUE klass) {
317
398
  VALUE obj;
318
399
  mysql_client_wrapper * wrapper;
400
+ #ifdef NEW_TYPEDDATA_WRAPPER
401
+ obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
402
+ #else
319
403
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
404
+ #endif
320
405
  wrapper->encoding = Qnil;
321
- wrapper->active_thread = Qnil;
406
+ wrapper->active_fiber = Qnil;
322
407
  wrapper->automatic_close = 1;
323
408
  wrapper->server_version = 0;
324
409
  wrapper->reconnect_enabled = 0;
325
410
  wrapper->connect_timeout = 0;
326
- wrapper->initialized = 0; /* means that that the wrapper is initialized */
411
+ wrapper->initialized = 0; /* will be set true after calling mysql_init */
412
+ wrapper->closed = 1; /* will be set false after calling mysql_real_connect */
327
413
  wrapper->refcount = 1;
328
- wrapper->closed = 0;
329
414
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
330
415
 
331
416
  return obj;
@@ -467,6 +552,7 @@ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VA
467
552
  rb_raise_mysql2_error(wrapper);
468
553
  }
469
554
 
555
+ wrapper->closed = 0;
470
556
  wrapper->server_version = mysql_get_server_version(wrapper->client);
471
557
  return self;
472
558
  }
@@ -513,12 +599,12 @@ static void *nogvl_send_query(void *ptr) {
513
599
  return (void*)(rv == 0 ? Qtrue : Qfalse);
514
600
  }
515
601
 
516
- static VALUE do_send_query(void *args) {
517
- struct nogvl_send_query_args *query_args = args;
602
+ static VALUE do_send_query(VALUE args) {
603
+ struct nogvl_send_query_args *query_args = (void *)args;
518
604
  mysql_client_wrapper *wrapper = query_args->wrapper;
519
- if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
605
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
520
606
  /* an error occurred, we're not active anymore */
521
- wrapper->active_thread = Qnil;
607
+ wrapper->active_fiber = Qnil;
522
608
  rb_raise_mysql2_error(wrapper);
523
609
  }
524
610
  return Qnil;
@@ -548,7 +634,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
548
634
 
549
635
  /* once our result is stored off, this connection is
550
636
  ready for another command to be issued */
551
- wrapper->active_thread = Qnil;
637
+ wrapper->active_fiber = Qnil;
552
638
 
553
639
  return result;
554
640
  }
@@ -574,13 +660,13 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
574
660
  GET_CLIENT(self);
575
661
 
576
662
  /* if we're not waiting on a result, do nothing */
577
- if (NIL_P(wrapper->active_thread))
663
+ if (NIL_P(wrapper->active_fiber))
578
664
  return Qnil;
579
665
 
580
666
  REQUIRE_CONNECTED(wrapper);
581
667
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
582
668
  /* an error occurred, mark this connection inactive */
583
- wrapper->active_thread = Qnil;
669
+ wrapper->active_fiber = Qnil;
584
670
  rb_raise_mysql2_error(wrapper);
585
671
  }
586
672
 
@@ -593,7 +679,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
593
679
 
594
680
  if (result == NULL) {
595
681
  if (mysql_errno(wrapper->client) != 0) {
596
- wrapper->active_thread = Qnil;
682
+ wrapper->active_fiber = Qnil;
597
683
  rb_raise_mysql2_error(wrapper);
598
684
  }
599
685
  /* no data and no error, so query was not a SELECT */
@@ -620,7 +706,7 @@ struct async_query_args {
620
706
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
621
707
  GET_CLIENT(self);
622
708
 
623
- wrapper->active_thread = Qnil;
709
+ wrapper->active_fiber = Qnil;
624
710
 
625
711
  /* Invalidate the MySQL socket to prevent further communication.
626
712
  * The GC will come along later and call mysql_close to free it.
@@ -636,8 +722,8 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
636
722
  rb_exc_raise(error);
637
723
  }
638
724
 
639
- static VALUE do_query(void *args) {
640
- struct async_query_args *async_args = args;
725
+ static VALUE do_query(VALUE args) {
726
+ struct async_query_args *async_args = (void *)args;
641
727
  struct timeval tv;
642
728
  struct timeval *tvp;
643
729
  long int sec;
@@ -685,7 +771,7 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
685
771
  GET_CLIENT(self);
686
772
 
687
773
  /* Check if execution terminated while result was still being read. */
688
- if (!NIL_P(wrapper->active_thread)) {
774
+ if (!NIL_P(wrapper->active_fiber)) {
689
775
  if (CONNECTED(wrapper)) {
690
776
  /* Invalidate the MySQL socket to prevent further communication. */
691
777
  #ifndef _WIN32
@@ -700,24 +786,24 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
700
786
  }
701
787
  /* Skip mysql client check performed before command execution. */
702
788
  wrapper->client->status = MYSQL_STATUS_READY;
703
- wrapper->active_thread = Qnil;
789
+ wrapper->active_fiber = Qnil;
704
790
  }
705
791
 
706
792
  return Qnil;
707
793
  }
708
794
 
709
- void rb_mysql_client_set_active_thread(VALUE self) {
710
- VALUE thread_current = rb_thread_current();
795
+ static void rb_mysql_client_set_active_fiber(VALUE self) {
796
+ VALUE fiber_current = rb_fiber_current();
711
797
  GET_CLIENT(self);
712
798
 
713
799
  // see if this connection is still waiting on a result from a previous query
714
- if (NIL_P(wrapper->active_thread)) {
800
+ if (NIL_P(wrapper->active_fiber)) {
715
801
  // mark this connection active
716
- wrapper->active_thread = thread_current;
717
- } else if (wrapper->active_thread == thread_current) {
802
+ wrapper->active_fiber = fiber_current;
803
+ } else if (wrapper->active_fiber == fiber_current) {
718
804
  rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
719
805
  } else {
720
- VALUE inspect = rb_inspect(wrapper->active_thread);
806
+ VALUE inspect = rb_inspect(wrapper->active_fiber);
721
807
  const char *thr = StringValueCStr(inspect);
722
808
 
723
809
  rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
@@ -781,10 +867,11 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
781
867
  args.sql_len = RSTRING_LEN(args.sql);
782
868
  args.wrapper = wrapper;
783
869
 
784
- rb_mysql_client_set_active_thread(self);
870
+ rb_mysql_client_set_active_fiber(self);
785
871
 
786
872
  #ifndef _WIN32
787
873
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
874
+ (void)RB_GC_GUARD(sql);
788
875
 
789
876
  if (rb_hash_aref(current, sym_async) == Qtrue) {
790
877
  return Qnil;
@@ -797,7 +884,8 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
797
884
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
798
885
  }
799
886
  #else
800
- do_send_query(&args);
887
+ do_send_query((VALUE)&args);
888
+ (void)RB_GC_GUARD(sql);
801
889
 
802
890
  /* this will just block until the result is ready */
803
891
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
@@ -908,10 +996,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
908
996
  retval = charval;
909
997
  break;
910
998
 
999
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
911
1000
  case MYSQL_DEFAULT_AUTH:
912
1001
  charval = (const char *)StringValueCStr(value);
913
1002
  retval = charval;
914
1003
  break;
1004
+ #endif
915
1005
 
916
1006
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
917
1007
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
@@ -1021,6 +1111,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
1021
1111
  return ULL2NUM(mysql_insert_id(wrapper->client));
1022
1112
  }
1023
1113
 
1114
+ /* call-seq:
1115
+ * client.session_track
1116
+ *
1117
+ * Returns information about changes to the session state on the server.
1118
+ */
1119
+ static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
1120
+ #ifdef CLIENT_SESSION_TRACK
1121
+ const char *data;
1122
+ size_t length;
1123
+ my_ulonglong retVal;
1124
+ GET_CLIENT(self);
1125
+
1126
+ REQUIRE_CONNECTED(wrapper);
1127
+ retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
1128
+ if (retVal != 0) {
1129
+ return Qnil;
1130
+ }
1131
+ VALUE rbAry = rb_ary_new();
1132
+ VALUE rbFirst = rb_str_new(data, length);
1133
+ rb_ary_push(rbAry, rbFirst);
1134
+ while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
1135
+ VALUE rbNext = rb_str_new(data, length);
1136
+ rb_ary_push(rbAry, rbNext);
1137
+ }
1138
+ return rbAry;
1139
+ #else
1140
+ return Qnil;
1141
+ #endif
1142
+ }
1143
+
1024
1144
  /* call-seq:
1025
1145
  * client.affected_rows
1026
1146
  *
@@ -1315,12 +1435,31 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1315
1435
  static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
1316
1436
  GET_CLIENT(self);
1317
1437
 
1438
+ #ifdef HAVE_MYSQL_SSL_SET
1318
1439
  mysql_ssl_set(wrapper->client,
1319
1440
  NIL_P(key) ? NULL : StringValueCStr(key),
1320
1441
  NIL_P(cert) ? NULL : StringValueCStr(cert),
1321
1442
  NIL_P(ca) ? NULL : StringValueCStr(ca),
1322
1443
  NIL_P(capath) ? NULL : StringValueCStr(capath),
1323
1444
  NIL_P(cipher) ? NULL : StringValueCStr(cipher));
1445
+ #else
1446
+ /* mysql 8.3 does not provide mysql_ssl_set */
1447
+ if (!NIL_P(key)) {
1448
+ mysql_options(wrapper->client, MYSQL_OPT_SSL_KEY, StringValueCStr(key));
1449
+ }
1450
+ if (!NIL_P(cert)) {
1451
+ mysql_options(wrapper->client, MYSQL_OPT_SSL_CERT, StringValueCStr(cert));
1452
+ }
1453
+ if (!NIL_P(ca)) {
1454
+ mysql_options(wrapper->client, MYSQL_OPT_SSL_CA, StringValueCStr(ca));
1455
+ }
1456
+ if (!NIL_P(capath)) {
1457
+ mysql_options(wrapper->client, MYSQL_OPT_SSL_CAPATH, StringValueCStr(capath));
1458
+ }
1459
+ if (!NIL_P(cipher)) {
1460
+ mysql_options(wrapper->client, MYSQL_OPT_SSL_CIPHER, StringValueCStr(cipher));
1461
+ }
1462
+ #endif
1324
1463
 
1325
1464
  return self;
1326
1465
  }
@@ -1347,7 +1486,11 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1347
1486
  }
1348
1487
 
1349
1488
  static VALUE set_default_auth(VALUE self, VALUE value) {
1489
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1350
1490
  return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1491
+ #else
1492
+ rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
1493
+ #endif
1351
1494
  }
1352
1495
 
1353
1496
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
@@ -1411,6 +1554,7 @@ void init_mysql2_client() {
1411
1554
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1412
1555
  #endif
1413
1556
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1557
+ rb_global_variable(&cMysql2Client);
1414
1558
 
1415
1559
  rb_define_alloc_func(cMysql2Client, allocate);
1416
1560
 
@@ -1441,6 +1585,7 @@ void init_mysql2_client() {
1441
1585
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1442
1586
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1443
1587
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1588
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1444
1589
 
1445
1590
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1446
1591
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1613,16 +1758,32 @@ void init_mysql2_client() {
1613
1758
  INT2NUM(0));
1614
1759
  #endif
1615
1760
 
1616
- #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1761
+ #ifdef CLIENT_SESSION_TRACK
1762
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1763
+ /* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1764
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1765
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1766
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1767
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1768
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1769
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1770
+ #endif
1771
+
1772
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.6.36 and MySQL 5.7.11 and above
1617
1773
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1618
1774
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1619
1775
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1620
1776
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1621
1777
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1622
- #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1778
+ #else
1779
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1780
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1781
+ #endif
1782
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1623
1783
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1624
1784
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1625
1785
  #endif
1786
+ #endif
1626
1787
 
1627
1788
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1628
1789
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
data/ext/mysql2/client.h CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  typedef struct {
5
5
  VALUE encoding;
6
- VALUE active_thread; /* rb_thread_current() or Qnil */
6
+ VALUE active_fiber; /* rb_fiber_current() or Qnil */
7
7
  long server_version;
8
8
  int reconnect_enabled;
9
9
  unsigned int connect_timeout;
@@ -15,12 +15,19 @@ typedef struct {
15
15
  MYSQL *client;
16
16
  } mysql_client_wrapper;
17
17
 
18
- void rb_mysql_client_set_active_thread(VALUE self);
19
18
  void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
20
19
 
20
+ extern const rb_data_type_t rb_mysql_client_type;
21
+
22
+ #ifdef NEW_TYPEDDATA_WRAPPER
23
+ #define GET_CLIENT(self) \
24
+ mysql_client_wrapper *wrapper; \
25
+ TypedData_Get_Struct(self, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
26
+ #else
21
27
  #define GET_CLIENT(self) \
22
28
  mysql_client_wrapper *wrapper; \
23
29
  Data_Get_Struct(self, mysql_client_wrapper, wrapper);
30
+ #endif
24
31
 
25
32
  void init_mysql2_client(void);
26
33
  void decr_mysql2_client(mysql_client_wrapper *wrapper);
@@ -1,6 +1,8 @@
1
1
  require 'mkmf'
2
2
  require 'English'
3
3
 
4
+ ### Some helper functions
5
+
4
6
  def asplode(lib)
5
7
  if RUBY_PLATFORM =~ /mingw|mswin/
6
8
  abort "-----\n#{lib} is missing. Check your installation of MySQL or Connector/C, and try again.\n-----"
@@ -15,19 +17,57 @@ def add_ssl_defines(header)
15
17
  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|
16
18
  m && have_const(ssl_mode, header)
17
19
  end
18
- $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
19
- # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
20
- has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
21
- $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
20
+ if all_modes_found
21
+ $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT'
22
+ else
23
+ # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
24
+ # and the verify server cert option. This is also the case for MariaDB.
25
+ has_verify_support = have_const('MYSQL_OPT_SSL_VERIFY_SERVER_CERT', header)
26
+ has_enforce_support = have_const('MYSQL_OPT_SSL_ENFORCE', header)
27
+ $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if !has_verify_support && !has_enforce_support
28
+ end
22
29
  end
23
30
 
31
+ ### Check for Ruby C extention interfaces
32
+
24
33
  # 2.1+
25
34
  have_func('rb_absint_size')
26
35
  have_func('rb_absint_singlebit_p')
27
36
 
37
+ # 2.7+
38
+ have_func('rb_gc_mark_movable')
39
+
28
40
  # Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
29
41
  have_func('rb_wait_for_single_fd')
30
42
 
43
+ # 3.0+
44
+ have_func('rb_enc_interned_str', 'ruby.h')
45
+
46
+ ### Find OpenSSL library
47
+
48
+ # User-specified OpenSSL if explicitly specified
49
+ if with_config('openssl-dir')
50
+ _, lib = dir_config('openssl')
51
+ if lib
52
+ # Ruby versions below 2.0 on Unix and below 2.1 on Windows
53
+ # do not properly search for lib directories, and must be corrected:
54
+ # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
55
+ unless lib && lib[-3, 3] == 'lib'
56
+ @libdir_basename = 'lib'
57
+ _, lib = dir_config('openssl')
58
+ end
59
+ abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
60
+ warn "-----\nUsing --with-openssl-dir=#{File.dirname lib}\n-----"
61
+ $LDFLAGS << " -L#{lib}"
62
+ end
63
+ # Homebrew OpenSSL on MacOS
64
+ elsif RUBY_PLATFORM =~ /darwin/ && system('command -v brew')
65
+ openssl_location = `brew --prefix openssl`.strip
66
+ $LDFLAGS << " -L#{openssl_location}/lib" if openssl_location
67
+ end
68
+
69
+ ### Find MySQL client library
70
+
31
71
  # borrowed from mysqlplus
32
72
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
33
73
  dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
@@ -35,6 +75,7 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
35
75
  /opt/local
36
76
  /opt/local/mysql
37
77
  /opt/local/lib/mysql5*
78
+ /opt/homebrew/opt/mysql*
38
79
  /usr
39
80
  /usr/mysql
40
81
  /usr/local
@@ -42,6 +83,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
42
83
  /usr/local/mysql-*
43
84
  /usr/local/lib/mysql5*
44
85
  /usr/local/opt/mysql5*
86
+ /usr/local/opt/mysql@*
87
+ /usr/local/opt/mysql-client
88
+ /usr/local/opt/mysql-client@*
45
89
  ].map { |dir| dir << '/bin' }
46
90
 
47
91
  # For those without HOMEBREW_ROOT in PATH
@@ -108,6 +152,7 @@ have_struct_member('MYSQL', 'net.vio', mysql_h)
108
152
  have_struct_member('MYSQL', 'net.pvio', mysql_h)
109
153
 
110
154
  # These constants are actually enums, so they cannot be detected by #ifdef in C code.
155
+ have_const('MYSQL_DEFAULT_AUTH', mysql_h)
111
156
  have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
112
157
  have_const('SERVER_QUERY_NO_GOOD_INDEX_USED', mysql_h)
113
158
  have_const('SERVER_QUERY_NO_INDEX_USED', mysql_h)
@@ -119,10 +164,16 @@ have_const('MYSQL_OPTION_MULTI_STATEMENTS_OFF', mysql_h)
119
164
  # to retain compatibility with the typedef in earlier MySQLs.
120
165
  have_type('my_bool', mysql_h)
121
166
 
167
+ # detect mysql functions
168
+ have_func('mysql_ssl_set', mysql_h)
169
+
170
+ ### Compiler flags to help catch errors
171
+
122
172
  # This is our wishlist. We use whichever flags work on the host.
123
173
  # -Wall and -Wextra are included by default.
124
174
  wishlist = [
125
175
  '-Weverything',
176
+ '-Wno-compound-token-split-by-macro', # Fixed in Ruby 2.7+ at https://bugs.ruby-lang.org/issues/17865
126
177
  '-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
127
178
  '-Wno-conditional-uninitialized', # false positive in client.c
128
179
  '-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
@@ -147,8 +198,10 @@ end
147
198
 
148
199
  $CFLAGS << ' ' << usable_flags.join(' ')
149
200
 
201
+ ### Sanitizers to help with debugging -- many are available on both Clang/LLVM and GCC
202
+
150
203
  enabled_sanitizers = disabled_sanitizers = []
151
- # Specify a commna-separated list of sanitizers, or try them all by default
204
+ # Specify a comma-separated list of sanitizers, or try them all by default
152
205
  sanitizers = with_config('sanitize')
153
206
  case sanitizers
154
207
  when true
@@ -164,7 +217,7 @@ when String
164
217
  end
165
218
  end
166
219
 
167
- unless disabled_sanitizers.empty?
220
+ unless disabled_sanitizers.empty? # rubocop:disable Style/IfUnlessModifier
168
221
  abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
169
222
  end
170
223
 
@@ -181,6 +234,8 @@ unless enabled_sanitizers.empty?
181
234
  $CFLAGS << ' -g -fno-omit-frame-pointer'
182
235
  end
183
236
 
237
+ ### Find MySQL Client on Windows, set RPATH to find the library at runtime
238
+
184
239
  if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
185
240
  # Build libmysql.a interface link library
186
241
  require 'rake'