mysql2 0.5.3 → 0.5.5

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
@@ -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
  *
@@ -1347,7 +1467,11 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1347
1467
  }
1348
1468
 
1349
1469
  static VALUE set_default_auth(VALUE self, VALUE value) {
1470
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1350
1471
  return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1472
+ #else
1473
+ rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
1474
+ #endif
1351
1475
  }
1352
1476
 
1353
1477
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
@@ -1411,6 +1535,7 @@ void init_mysql2_client() {
1411
1535
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1412
1536
  #endif
1413
1537
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1538
+ rb_global_variable(&cMysql2Client);
1414
1539
 
1415
1540
  rb_define_alloc_func(cMysql2Client, allocate);
1416
1541
 
@@ -1441,6 +1566,7 @@ void init_mysql2_client() {
1441
1566
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1442
1567
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1443
1568
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1569
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1444
1570
 
1445
1571
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1446
1572
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1613,16 +1739,32 @@ void init_mysql2_client() {
1613
1739
  INT2NUM(0));
1614
1740
  #endif
1615
1741
 
1616
- #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1742
+ #ifdef CLIENT_SESSION_TRACK
1743
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1744
+ /* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1745
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1746
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1747
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1748
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1749
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1750
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1751
+ #endif
1752
+
1753
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.6.36 and MySQL 5.7.11 and above
1617
1754
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1618
1755
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1619
1756
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1620
1757
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1621
1758
  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
1759
+ #else
1760
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1761
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1762
+ #endif
1763
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1623
1764
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1624
1765
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1625
1766
  #endif
1767
+ #endif
1626
1768
 
1627
1769
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1628
1770
  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,13 @@ 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
+ ### Compiler flags to help catch errors
168
+
122
169
  # This is our wishlist. We use whichever flags work on the host.
123
170
  # -Wall and -Wextra are included by default.
124
171
  wishlist = [
125
172
  '-Weverything',
173
+ '-Wno-compound-token-split-by-macro', # Fixed in Ruby 2.7+ at https://bugs.ruby-lang.org/issues/17865
126
174
  '-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
127
175
  '-Wno-conditional-uninitialized', # false positive in client.c
128
176
  '-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
@@ -147,8 +195,10 @@ end
147
195
 
148
196
  $CFLAGS << ' ' << usable_flags.join(' ')
149
197
 
198
+ ### Sanitizers to help with debugging -- many are available on both Clang/LLVM and GCC
199
+
150
200
  enabled_sanitizers = disabled_sanitizers = []
151
- # Specify a commna-separated list of sanitizers, or try them all by default
201
+ # Specify a comma-separated list of sanitizers, or try them all by default
152
202
  sanitizers = with_config('sanitize')
153
203
  case sanitizers
154
204
  when true
@@ -164,7 +214,7 @@ when String
164
214
  end
165
215
  end
166
216
 
167
- unless disabled_sanitizers.empty?
217
+ unless disabled_sanitizers.empty? # rubocop:disable Style/IfUnlessModifier
168
218
  abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
169
219
  end
170
220
 
@@ -181,6 +231,8 @@ unless enabled_sanitizers.empty?
181
231
  $CFLAGS << ' -g -fno-omit-frame-pointer'
182
232
  end
183
233
 
234
+ ### Find MySQL Client on Windows, set RPATH to find the library at runtime
235
+
184
236
  if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
185
237
  # Build libmysql.a interface link library
186
238
  require 'rake'
@@ -4,9 +4,14 @@ VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
4
4
 
5
5
  /* Ruby Extension initializer */
6
6
  void Init_mysql2() {
7
- mMysql2 = rb_define_module("Mysql2");
7
+ mMysql2 = rb_define_module("Mysql2");
8
+ rb_global_variable(&mMysql2);
9
+
8
10
  cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
11
+ rb_global_variable(&cMysql2Error);
12
+
9
13
  cMysql2TimeoutError = rb_const_get(cMysql2Error, rb_intern("TimeoutError"));
14
+ rb_global_variable(&cMysql2TimeoutError);
10
15
 
11
16
  init_mysql2_client();
12
17
  init_mysql2_result();
@@ -36,6 +36,19 @@ void Init_mysql2(void);
36
36
  typedef bool my_bool;
37
37
  #endif
38
38
 
39
+ // ruby 2.7+
40
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
41
+ #define rb_mysql2_gc_location(ptr) ptr = rb_gc_location(ptr)
42
+ #else
43
+ #define rb_gc_mark_movable(ptr) rb_gc_mark(ptr)
44
+ #define rb_mysql2_gc_location(ptr)
45
+ #endif
46
+
47
+ // ruby 2.2+
48
+ #ifdef TypedData_Make_Struct
49
+ #define NEW_TYPEDDATA_WRAPPER 1
50
+ #endif
51
+
39
52
  #include <client.h>
40
53
  #include <statement.h>
41
54
  #include <result.h>