mysql2 0.5.2 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +145 -44
  3. data/ext/mysql2/client.c +236 -58
  4. data/ext/mysql2/client.h +9 -2
  5. data/ext/mysql2/extconf.rb +62 -7
  6. data/ext/mysql2/mysql2_ext.c +6 -1
  7. data/ext/mysql2/mysql2_ext.h +13 -0
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -55
  9. data/ext/mysql2/mysql_enc_to_ruby.h +71 -5
  10. data/ext/mysql2/result.c +287 -22
  11. data/ext/mysql2/result.h +1 -0
  12. data/ext/mysql2/statement.c +63 -14
  13. data/lib/mysql2/client.rb +24 -3
  14. data/lib/mysql2/error.rb +4 -3
  15. data/lib/mysql2/statement.rb +1 -3
  16. data/lib/mysql2/version.rb +1 -1
  17. data/lib/mysql2.rb +8 -3
  18. data/support/3A79BD29.asc +49 -0
  19. data/support/5072E1F5.asc +5 -5
  20. data/support/C74CD1D8.asc +104 -0
  21. data/support/mysql_enc_to_ruby.rb +7 -1
  22. data/support/ruby_enc_to_mysql.rb +3 -0
  23. metadata +15 -59
  24. data/examples/eventmachine.rb +0 -19
  25. data/examples/threaded.rb +0 -16
  26. data/spec/configuration.yml.example +0 -11
  27. data/spec/em/em_spec.rb +0 -135
  28. data/spec/my.cnf.example +0 -9
  29. data/spec/mysql2/client_spec.rb +0 -1072
  30. data/spec/mysql2/error_spec.rb +0 -78
  31. data/spec/mysql2/result_spec.rb +0 -485
  32. data/spec/mysql2/statement_spec.rb +0 -712
  33. data/spec/rcov.opts +0 -3
  34. data/spec/spec_helper.rb +0 -112
  35. data/spec/ssl/ca-cert.pem +0 -17
  36. data/spec/ssl/ca-key.pem +0 -27
  37. data/spec/ssl/ca.cnf +0 -22
  38. data/spec/ssl/cert.cnf +0 -22
  39. data/spec/ssl/client-cert.pem +0 -17
  40. data/spec/ssl/client-key.pem +0 -27
  41. data/spec/ssl/client-req.pem +0 -15
  42. data/spec/ssl/gen_certs.sh +0 -48
  43. data/spec/ssl/pkcs8-client-key.pem +0 -28
  44. data/spec/ssl/pkcs8-server-key.pem +0 -28
  45. data/spec/ssl/server-cert.pem +0 -17
  46. data/spec/ssl/server-key.pem +0 -27
  47. data/spec/ssl/server-req.pem +0 -15
  48. data/spec/test_data +0 -1
data/ext/mysql2/client.c CHANGED
@@ -18,7 +18,8 @@ VALUE cMysql2Client;
18
18
  extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
19
19
  static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
20
20
  static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
21
- static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
22
+ intern_current_query_options, intern_read_timeout;
22
23
 
23
24
  #define REQUIRE_INITIALIZED(wrapper) \
24
25
  if (!wrapper->initialized) { \
@@ -44,7 +45,7 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
44
45
  }
45
46
 
46
47
  /*
47
- * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
49
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
49
50
  * linking against the server itself
50
51
  */
@@ -57,8 +58,23 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
57
58
  #endif
58
59
 
59
60
  /*
60
- * 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.
61
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
62
78
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
63
79
  #define SSL_MODE_DISABLED 1
64
80
  #define SSL_MODE_REQUIRED 3
@@ -104,40 +120,80 @@ struct nogvl_select_db_args {
104
120
 
105
121
  static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
106
122
  unsigned long version = mysql_get_client_version();
123
+ const char *version_str = mysql_get_client_info();
107
124
 
108
- if (version < 50703) {
109
- 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);
110
133
  return Qnil;
111
134
  }
112
- #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)
113
142
  GET_CLIENT(self);
114
- int val = NUM2INT( setting );
115
- if (version >= 50703 && version < 50711) {
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
116
160
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
117
- my_bool b = ( val == SSL_MODE_REQUIRED );
118
- 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);
119
163
  return INT2NUM(result);
120
- } else {
121
- rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
122
- return Qnil;
123
164
  }
165
+ #endif
166
+ rb_warn("Your mysql client library version %s does not support ssl_mode %d", version_str, val);
167
+ return Qnil;
168
+ } else {
169
+ rb_warn("Your mysql client library version %s does not support ssl_mode as expected", version_str);
170
+ return Qnil;
124
171
  }
125
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
+ */
126
178
  #ifdef FULL_SSL_MODE_SUPPORT
127
179
  GET_CLIENT(self);
128
- int val = NUM2INT( setting );
180
+ int val = NUM2INT(setting);
129
181
 
130
182
  if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
131
183
  rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
132
184
  }
133
- int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
185
+ int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_MODE, &val);
134
186
 
135
187
  return INT2NUM(result);
136
188
  #endif
189
+
190
+ // Warn if we get this far
137
191
  #ifdef NO_SSL_MODE_SUPPORT
192
+ rb_warn("Your mysql client library does not support setting ssl_mode");
138
193
  return Qnil;
139
194
  #endif
140
195
  }
196
+
141
197
  /*
142
198
  * non-blocking mysql_*() functions that we won't be wrapping since
143
199
  * they do not appear to hit the network nor issue any interruptible
@@ -163,14 +219,50 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
163
219
  static void rb_mysql_client_mark(void * wrapper) {
164
220
  mysql_client_wrapper * w = wrapper;
165
221
  if (w) {
166
- rb_gc_mark(w->encoding);
167
- 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);
168
243
  }
169
244
  }
170
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
+
171
263
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
172
264
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
173
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
265
+ VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
174
266
  VALUE e;
175
267
 
176
268
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
@@ -264,22 +356,16 @@ static VALUE invalidate_fd(int clientfd)
264
356
  static void *nogvl_close(void *ptr) {
265
357
  mysql_client_wrapper *wrapper = ptr;
266
358
 
267
- if (!wrapper->closed) {
359
+ if (wrapper->initialized && !wrapper->closed) {
268
360
  mysql_close(wrapper->client);
269
361
  wrapper->closed = 1;
270
362
  wrapper->reconnect_enabled = 0;
271
- wrapper->active_thread = Qnil;
363
+ wrapper->active_fiber = Qnil;
272
364
  }
273
365
 
274
366
  return NULL;
275
367
  }
276
368
 
277
- /* this is called during GC */
278
- static void rb_mysql_client_free(void *ptr) {
279
- mysql_client_wrapper *wrapper = ptr;
280
- decr_mysql2_client(wrapper);
281
- }
282
-
283
369
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
284
370
  {
285
371
  wrapper->refcount--;
@@ -311,16 +397,20 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
311
397
  static VALUE allocate(VALUE klass) {
312
398
  VALUE obj;
313
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
314
403
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
404
+ #endif
315
405
  wrapper->encoding = Qnil;
316
- wrapper->active_thread = Qnil;
406
+ wrapper->active_fiber = Qnil;
317
407
  wrapper->automatic_close = 1;
318
408
  wrapper->server_version = 0;
319
409
  wrapper->reconnect_enabled = 0;
320
410
  wrapper->connect_timeout = 0;
321
- 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 */
322
413
  wrapper->refcount = 1;
323
- wrapper->closed = 0;
324
414
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
325
415
 
326
416
  return obj;
@@ -462,6 +552,7 @@ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VA
462
552
  rb_raise_mysql2_error(wrapper);
463
553
  }
464
554
 
555
+ wrapper->closed = 0;
465
556
  wrapper->server_version = mysql_get_server_version(wrapper->client);
466
557
  return self;
467
558
  }
@@ -508,12 +599,12 @@ static void *nogvl_send_query(void *ptr) {
508
599
  return (void*)(rv == 0 ? Qtrue : Qfalse);
509
600
  }
510
601
 
511
- static VALUE do_send_query(void *args) {
512
- 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;
513
604
  mysql_client_wrapper *wrapper = query_args->wrapper;
514
- 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) {
515
606
  /* an error occurred, we're not active anymore */
516
- wrapper->active_thread = Qnil;
607
+ wrapper->active_fiber = Qnil;
517
608
  rb_raise_mysql2_error(wrapper);
518
609
  }
519
610
  return Qnil;
@@ -543,7 +634,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
543
634
 
544
635
  /* once our result is stored off, this connection is
545
636
  ready for another command to be issued */
546
- wrapper->active_thread = Qnil;
637
+ wrapper->active_fiber = Qnil;
547
638
 
548
639
  return result;
549
640
  }
@@ -569,17 +660,17 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
569
660
  GET_CLIENT(self);
570
661
 
571
662
  /* if we're not waiting on a result, do nothing */
572
- if (NIL_P(wrapper->active_thread))
663
+ if (NIL_P(wrapper->active_fiber))
573
664
  return Qnil;
574
665
 
575
666
  REQUIRE_CONNECTED(wrapper);
576
667
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
577
668
  /* an error occurred, mark this connection inactive */
578
- wrapper->active_thread = Qnil;
669
+ wrapper->active_fiber = Qnil;
579
670
  rb_raise_mysql2_error(wrapper);
580
671
  }
581
672
 
582
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
673
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
583
674
  if (is_streaming == Qtrue) {
584
675
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
585
676
  } else {
@@ -588,7 +679,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
588
679
 
589
680
  if (result == NULL) {
590
681
  if (mysql_errno(wrapper->client) != 0) {
591
- wrapper->active_thread = Qnil;
682
+ wrapper->active_fiber = Qnil;
592
683
  rb_raise_mysql2_error(wrapper);
593
684
  }
594
685
  /* no data and no error, so query was not a SELECT */
@@ -596,7 +687,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
596
687
  }
597
688
 
598
689
  // Duplicate the options hash and put the copy in the Result object
599
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
690
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
600
691
  (void)RB_GC_GUARD(current);
601
692
  Check_Type(current, T_HASH);
602
693
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -615,7 +706,7 @@ struct async_query_args {
615
706
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
616
707
  GET_CLIENT(self);
617
708
 
618
- wrapper->active_thread = Qnil;
709
+ wrapper->active_fiber = Qnil;
619
710
 
620
711
  /* Invalidate the MySQL socket to prevent further communication.
621
712
  * The GC will come along later and call mysql_close to free it.
@@ -631,15 +722,15 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
631
722
  rb_exc_raise(error);
632
723
  }
633
724
 
634
- static VALUE do_query(void *args) {
635
- struct async_query_args *async_args = args;
725
+ static VALUE do_query(VALUE args) {
726
+ struct async_query_args *async_args = (void *)args;
636
727
  struct timeval tv;
637
728
  struct timeval *tvp;
638
729
  long int sec;
639
730
  int retval;
640
731
  VALUE read_timeout;
641
732
 
642
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
733
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
643
734
 
644
735
  tvp = NULL;
645
736
  if (!NIL_P(read_timeout)) {
@@ -680,7 +771,7 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
680
771
  GET_CLIENT(self);
681
772
 
682
773
  /* Check if execution terminated while result was still being read. */
683
- if (!NIL_P(wrapper->active_thread)) {
774
+ if (!NIL_P(wrapper->active_fiber)) {
684
775
  if (CONNECTED(wrapper)) {
685
776
  /* Invalidate the MySQL socket to prevent further communication. */
686
777
  #ifndef _WIN32
@@ -695,24 +786,24 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
695
786
  }
696
787
  /* Skip mysql client check performed before command execution. */
697
788
  wrapper->client->status = MYSQL_STATUS_READY;
698
- wrapper->active_thread = Qnil;
789
+ wrapper->active_fiber = Qnil;
699
790
  }
700
791
 
701
792
  return Qnil;
702
793
  }
703
794
 
704
- void rb_mysql_client_set_active_thread(VALUE self) {
705
- VALUE thread_current = rb_thread_current();
795
+ static void rb_mysql_client_set_active_fiber(VALUE self) {
796
+ VALUE fiber_current = rb_fiber_current();
706
797
  GET_CLIENT(self);
707
798
 
708
799
  // see if this connection is still waiting on a result from a previous query
709
- if (NIL_P(wrapper->active_thread)) {
800
+ if (NIL_P(wrapper->active_fiber)) {
710
801
  // mark this connection active
711
- wrapper->active_thread = thread_current;
712
- } else if (wrapper->active_thread == thread_current) {
802
+ wrapper->active_fiber = fiber_current;
803
+ } else if (wrapper->active_fiber == fiber_current) {
713
804
  rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
714
805
  } else {
715
- VALUE inspect = rb_inspect(wrapper->active_thread);
806
+ VALUE inspect = rb_inspect(wrapper->active_fiber);
716
807
  const char *thr = StringValueCStr(inspect);
717
808
 
718
809
  rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
@@ -767,7 +858,7 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
767
858
 
768
859
  (void)RB_GC_GUARD(current);
769
860
  Check_Type(current, T_HASH);
770
- rb_iv_set(self, "@current_query_options", current);
861
+ rb_ivar_set(self, intern_current_query_options, current);
771
862
 
772
863
  Check_Type(sql, T_STRING);
773
864
  /* ensure the string is in the encoding the connection is expecting */
@@ -776,10 +867,11 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
776
867
  args.sql_len = RSTRING_LEN(args.sql);
777
868
  args.wrapper = wrapper;
778
869
 
779
- rb_mysql_client_set_active_thread(self);
870
+ rb_mysql_client_set_active_fiber(self);
780
871
 
781
872
  #ifndef _WIN32
782
873
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
874
+ (void)RB_GC_GUARD(sql);
783
875
 
784
876
  if (rb_hash_aref(current, sym_async) == Qtrue) {
785
877
  return Qnil;
@@ -792,7 +884,8 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
792
884
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
793
885
  }
794
886
  #else
795
- do_send_query(&args);
887
+ do_send_query((VALUE)&args);
888
+ (void)RB_GC_GUARD(sql);
796
889
 
797
890
  /* this will just block until the result is ready */
798
891
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
@@ -903,6 +996,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
903
996
  retval = charval;
904
997
  break;
905
998
 
999
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1000
+ case MYSQL_DEFAULT_AUTH:
1001
+ charval = (const char *)StringValueCStr(value);
1002
+ retval = charval;
1003
+ break;
1004
+ #endif
1005
+
906
1006
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
907
1007
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
908
1008
  boolval = (value == Qfalse ? 0 : 1);
@@ -1011,6 +1111,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
1011
1111
  return ULL2NUM(mysql_insert_id(wrapper->client));
1012
1112
  }
1013
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
+
1014
1144
  /* call-seq:
1015
1145
  * client.affected_rows
1016
1146
  *
@@ -1174,7 +1304,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1174
1304
  }
1175
1305
 
1176
1306
  // Duplicate the options hash and put the copy in the Result object
1177
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1307
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1178
1308
  (void)RB_GC_GUARD(current);
1179
1309
  Check_Type(current, T_HASH);
1180
1310
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -1260,7 +1390,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1260
1390
  /* Set the instance variable here even though _mysql_client_options
1261
1391
  might not succeed, because the timeout is used in other ways
1262
1392
  elsewhere */
1263
- rb_iv_set(self, "@read_timeout", value);
1393
+ rb_ivar_set(self, intern_read_timeout, value);
1264
1394
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1265
1395
  }
1266
1396
 
@@ -1305,12 +1435,31 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1305
1435
  static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
1306
1436
  GET_CLIENT(self);
1307
1437
 
1438
+ #ifdef HAVE_MYSQL_SSL_SET
1308
1439
  mysql_ssl_set(wrapper->client,
1309
1440
  NIL_P(key) ? NULL : StringValueCStr(key),
1310
1441
  NIL_P(cert) ? NULL : StringValueCStr(cert),
1311
1442
  NIL_P(ca) ? NULL : StringValueCStr(ca),
1312
1443
  NIL_P(capath) ? NULL : StringValueCStr(capath),
1313
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
1314
1463
 
1315
1464
  return self;
1316
1465
  }
@@ -1336,6 +1485,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1336
1485
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1337
1486
  }
1338
1487
 
1488
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1489
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
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
1494
+ }
1495
+
1339
1496
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
1497
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
1498
  return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
@@ -1397,6 +1554,7 @@ void init_mysql2_client() {
1397
1554
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1398
1555
  #endif
1399
1556
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1557
+ rb_global_variable(&cMysql2Client);
1400
1558
 
1401
1559
  rb_define_alloc_func(cMysql2Client, allocate);
1402
1560
 
@@ -1427,6 +1585,7 @@ void init_mysql2_client() {
1427
1585
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1428
1586
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1429
1587
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1588
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1430
1589
 
1431
1590
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1432
1591
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1437,6 +1596,7 @@ void init_mysql2_client() {
1437
1596
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1438
1597
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1439
1598
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1599
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1440
1600
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1441
1601
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1442
1602
  rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
@@ -1461,6 +1621,8 @@ void init_mysql2_client() {
1461
1621
  intern_merge = rb_intern("merge");
1462
1622
  intern_merge_bang = rb_intern("merge!");
1463
1623
  intern_new_with_args = rb_intern("new_with_args");
1624
+ intern_current_query_options = rb_intern("@current_query_options");
1625
+ intern_read_timeout = rb_intern("@read_timeout");
1464
1626
 
1465
1627
  #ifdef CLIENT_LONG_PASSWORD
1466
1628
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
@@ -1596,16 +1758,32 @@ void init_mysql2_client() {
1596
1758
  INT2NUM(0));
1597
1759
  #endif
1598
1760
 
1599
- #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
1600
1773
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1601
1774
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1602
1775
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1603
1776
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1604
1777
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1605
- #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
1606
1783
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1607
1784
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1608
1785
  #endif
1786
+ #endif
1609
1787
 
1610
1788
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1611
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);