mysql2 0.5.2 → 0.5.6

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