mysql2 0.4.10 → 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 (52) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +167 -48
  3. data/ext/mysql2/client.c +335 -108
  4. data/ext/mysql2/client.h +10 -41
  5. data/ext/mysql2/extconf.rb +84 -26
  6. data/ext/mysql2/mysql2_ext.c +8 -2
  7. data/ext/mysql2/mysql2_ext.h +21 -4
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -55
  9. data/ext/mysql2/mysql_enc_to_ruby.h +79 -3
  10. data/ext/mysql2/result.c +298 -92
  11. data/ext/mysql2/result.h +3 -3
  12. data/ext/mysql2/statement.c +137 -81
  13. data/ext/mysql2/statement.h +0 -2
  14. data/ext/mysql2/wait_for_single_fd.h +2 -1
  15. data/lib/mysql2/client.rb +55 -28
  16. data/lib/mysql2/em.rb +2 -4
  17. data/lib/mysql2/error.rb +52 -22
  18. data/lib/mysql2/result.rb +2 -0
  19. data/lib/mysql2/statement.rb +3 -11
  20. data/lib/mysql2/version.rb +1 -1
  21. data/lib/mysql2.rb +19 -15
  22. data/support/3A79BD29.asc +49 -0
  23. data/support/5072E1F5.asc +5 -5
  24. data/support/C74CD1D8.asc +104 -0
  25. data/support/mysql_enc_to_ruby.rb +9 -3
  26. data/support/ruby_enc_to_mysql.rb +8 -5
  27. metadata +19 -62
  28. data/examples/eventmachine.rb +0 -21
  29. data/examples/threaded.rb +0 -18
  30. data/spec/configuration.yml.example +0 -11
  31. data/spec/em/em_spec.rb +0 -136
  32. data/spec/my.cnf.example +0 -9
  33. data/spec/mysql2/client_spec.rb +0 -1039
  34. data/spec/mysql2/error_spec.rb +0 -82
  35. data/spec/mysql2/result_spec.rb +0 -545
  36. data/spec/mysql2/statement_spec.rb +0 -776
  37. data/spec/rcov.opts +0 -3
  38. data/spec/spec_helper.rb +0 -108
  39. data/spec/ssl/ca-cert.pem +0 -17
  40. data/spec/ssl/ca-key.pem +0 -27
  41. data/spec/ssl/ca.cnf +0 -22
  42. data/spec/ssl/cert.cnf +0 -22
  43. data/spec/ssl/client-cert.pem +0 -17
  44. data/spec/ssl/client-key.pem +0 -27
  45. data/spec/ssl/client-req.pem +0 -15
  46. data/spec/ssl/gen_certs.sh +0 -48
  47. data/spec/ssl/pkcs8-client-key.pem +0 -28
  48. data/spec/ssl/pkcs8-server-key.pem +0 -28
  49. data/spec/ssl/server-cert.pem +0 -17
  50. data/spec/ssl/server-key.pem +0 -27
  51. data/spec/ssl/server-req.pem +0 -15
  52. data/spec/test_data +0 -1
data/ext/mysql2/client.c CHANGED
@@ -15,15 +15,11 @@
15
15
  #include "mysql_enc_name_to_ruby.h"
16
16
 
17
17
  VALUE cMysql2Client;
18
- extern VALUE mMysql2, cMysql2Error;
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
- static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
-
22
- #ifndef HAVE_RB_HASH_DUP
23
- VALUE rb_hash_dup(VALUE other) {
24
- return rb_funcall(rb_cHash, intern_brackets, 1, other);
25
- }
26
- #endif
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,
22
+ intern_current_query_options, intern_read_timeout;
27
23
 
28
24
  #define REQUIRE_INITIALIZED(wrapper) \
29
25
  if (!wrapper->initialized) { \
@@ -49,7 +45,7 @@ VALUE rb_hash_dup(VALUE other) {
49
45
  }
50
46
 
51
47
  /*
52
- * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
53
49
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
54
50
  * linking against the server itself
55
51
  */
@@ -62,8 +58,23 @@ VALUE rb_hash_dup(VALUE other) {
62
58
  #endif
63
59
 
64
60
  /*
65
- * 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.
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.
66
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
67
78
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
68
79
  #define SSL_MODE_DISABLED 1
69
80
  #define SSL_MODE_REQUIRED 3
@@ -109,40 +120,80 @@ struct nogvl_select_db_args {
109
120
 
110
121
  static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
111
122
  unsigned long version = mysql_get_client_version();
123
+ const char *version_str = mysql_get_client_info();
112
124
 
113
- if (version < 50703) {
114
- 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);
115
133
  return Qnil;
116
134
  }
117
- #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)
118
142
  GET_CLIENT(self);
119
- int val = NUM2INT( setting );
120
- 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
121
160
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
122
- bool b = ( val == SSL_MODE_REQUIRED );
123
- int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
161
+ my_bool b = (val == SSL_MODE_REQUIRED);
162
+ int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b);
124
163
  return INT2NUM(result);
125
- } else {
126
- rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
127
- return Qnil;
128
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;
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,20 +219,54 @@ 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
- #ifdef HAVE_RUBY_ENCODING_H
182
268
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
183
269
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
184
- #endif
185
270
 
186
271
  e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
187
272
  rb_error_msg,
@@ -271,22 +356,16 @@ static VALUE invalidate_fd(int clientfd)
271
356
  static void *nogvl_close(void *ptr) {
272
357
  mysql_client_wrapper *wrapper = ptr;
273
358
 
274
- if (!wrapper->closed) {
359
+ if (wrapper->initialized && !wrapper->closed) {
275
360
  mysql_close(wrapper->client);
276
361
  wrapper->closed = 1;
277
362
  wrapper->reconnect_enabled = 0;
278
- wrapper->active_thread = Qnil;
363
+ wrapper->active_fiber = Qnil;
279
364
  }
280
365
 
281
366
  return NULL;
282
367
  }
283
368
 
284
- /* this is called during GC */
285
- static void rb_mysql_client_free(void *ptr) {
286
- mysql_client_wrapper *wrapper = ptr;
287
- decr_mysql2_client(wrapper);
288
- }
289
-
290
369
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
291
370
  {
292
371
  wrapper->refcount--;
@@ -318,16 +397,20 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
318
397
  static VALUE allocate(VALUE klass) {
319
398
  VALUE obj;
320
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
321
403
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
404
+ #endif
322
405
  wrapper->encoding = Qnil;
323
- wrapper->active_thread = Qnil;
406
+ wrapper->active_fiber = Qnil;
324
407
  wrapper->automatic_close = 1;
325
408
  wrapper->server_version = 0;
326
409
  wrapper->reconnect_enabled = 0;
327
410
  wrapper->connect_timeout = 0;
328
- 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 */
329
413
  wrapper->refcount = 1;
330
- wrapper->closed = 0;
331
414
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
332
415
 
333
416
  return obj;
@@ -357,9 +440,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
357
440
  return str;
358
441
  } else {
359
442
  rb_str = rb_str_new((const char*)newStr, newLen);
360
- #ifdef HAVE_RUBY_ENCODING_H
361
443
  rb_enc_copy(rb_str, str);
362
- #endif
363
444
  xfree(newStr);
364
445
  return rb_str;
365
446
  }
@@ -386,9 +467,7 @@ static VALUE rb_mysql_info(VALUE self) {
386
467
  }
387
468
 
388
469
  rb_str = rb_str_new2(info);
389
- #ifdef HAVE_RUBY_ENCODING_H
390
470
  rb_enc_associate(rb_str, rb_utf8_encoding());
391
- #endif
392
471
 
393
472
  return rb_str;
394
473
  }
@@ -406,14 +485,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
406
485
  }
407
486
 
408
487
  rb_str = rb_str_new2(cipher);
409
- #ifdef HAVE_RUBY_ENCODING_H
410
488
  rb_enc_associate(rb_str, rb_utf8_encoding());
411
- #endif
412
489
 
413
490
  return rb_str;
414
491
  }
415
492
 
416
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
493
+ #ifdef CLIENT_CONNECT_ATTRS
494
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
495
+ {
496
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
497
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
498
+ key = rb_str_export_to_enc(key, enc);
499
+ value = rb_str_export_to_enc(value, enc);
500
+
501
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
502
+ return ST_CONTINUE;
503
+ }
504
+ #endif
505
+
506
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
417
507
  struct nogvl_connect_args args;
418
508
  time_t start_time, end_time, elapsed_time, connect_timeout;
419
509
  VALUE rv;
@@ -428,6 +518,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
428
518
  args.mysql = wrapper->client;
429
519
  args.client_flag = NUM2ULONG(flags);
430
520
 
521
+ #ifdef CLIENT_CONNECT_ATTRS
522
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
523
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
524
+ #endif
525
+
431
526
  if (wrapper->connect_timeout)
432
527
  time(&start_time);
433
528
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -457,6 +552,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
457
552
  rb_raise_mysql2_error(wrapper);
458
553
  }
459
554
 
555
+ wrapper->closed = 0;
460
556
  wrapper->server_version = mysql_get_server_version(wrapper->client);
461
557
  return self;
462
558
  }
@@ -503,12 +599,12 @@ static void *nogvl_send_query(void *ptr) {
503
599
  return (void*)(rv == 0 ? Qtrue : Qfalse);
504
600
  }
505
601
 
506
- static VALUE do_send_query(void *args) {
507
- 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;
508
604
  mysql_client_wrapper *wrapper = query_args->wrapper;
509
- 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) {
510
606
  /* an error occurred, we're not active anymore */
511
- wrapper->active_thread = Qnil;
607
+ wrapper->active_fiber = Qnil;
512
608
  rb_raise_mysql2_error(wrapper);
513
609
  }
514
610
  return Qnil;
@@ -521,7 +617,7 @@ static VALUE do_send_query(void *args) {
521
617
  */
522
618
  static void *nogvl_read_query_result(void *ptr) {
523
619
  MYSQL * client = ptr;
524
- bool res = mysql_read_query_result(client);
620
+ my_bool res = mysql_read_query_result(client);
525
621
 
526
622
  return (void *)(res == 0 ? Qtrue : Qfalse);
527
623
  }
@@ -538,7 +634,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
538
634
 
539
635
  /* once our result is stored off, this connection is
540
636
  ready for another command to be issued */
541
- wrapper->active_thread = Qnil;
637
+ wrapper->active_fiber = Qnil;
542
638
 
543
639
  return result;
544
640
  }
@@ -564,17 +660,17 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
564
660
  GET_CLIENT(self);
565
661
 
566
662
  /* if we're not waiting on a result, do nothing */
567
- if (NIL_P(wrapper->active_thread))
663
+ if (NIL_P(wrapper->active_fiber))
568
664
  return Qnil;
569
665
 
570
666
  REQUIRE_CONNECTED(wrapper);
571
667
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
572
668
  /* an error occurred, mark this connection inactive */
573
- wrapper->active_thread = Qnil;
669
+ wrapper->active_fiber = Qnil;
574
670
  rb_raise_mysql2_error(wrapper);
575
671
  }
576
672
 
577
- 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);
578
674
  if (is_streaming == Qtrue) {
579
675
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
580
676
  } else {
@@ -583,18 +679,21 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
583
679
 
584
680
  if (result == NULL) {
585
681
  if (mysql_errno(wrapper->client) != 0) {
586
- wrapper->active_thread = Qnil;
682
+ wrapper->active_fiber = Qnil;
587
683
  rb_raise_mysql2_error(wrapper);
588
684
  }
589
685
  /* no data and no error, so query was not a SELECT */
590
686
  return Qnil;
591
687
  }
592
688
 
593
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
689
+ // Duplicate the options hash and put the copy in the Result object
690
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
594
691
  (void)RB_GC_GUARD(current);
595
692
  Check_Type(current, T_HASH);
596
693
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
597
694
 
695
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
696
+
598
697
  return resultObj;
599
698
  }
600
699
 
@@ -607,7 +706,7 @@ struct async_query_args {
607
706
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
608
707
  GET_CLIENT(self);
609
708
 
610
- wrapper->active_thread = Qnil;
709
+ wrapper->active_fiber = Qnil;
611
710
 
612
711
  /* Invalidate the MySQL socket to prevent further communication.
613
712
  * The GC will come along later and call mysql_close to free it.
@@ -623,15 +722,15 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
623
722
  rb_exc_raise(error);
624
723
  }
625
724
 
626
- static VALUE do_query(void *args) {
627
- struct async_query_args *async_args = args;
725
+ static VALUE do_query(VALUE args) {
726
+ struct async_query_args *async_args = (void *)args;
628
727
  struct timeval tv;
629
728
  struct timeval *tvp;
630
729
  long int sec;
631
730
  int retval;
632
731
  VALUE read_timeout;
633
732
 
634
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
733
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
635
734
 
636
735
  tvp = NULL;
637
736
  if (!NIL_P(read_timeout)) {
@@ -652,7 +751,7 @@ static VALUE do_query(void *args) {
652
751
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
653
752
 
654
753
  if (retval == 0) {
655
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
754
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
656
755
  }
657
756
 
658
757
  if (retval < 0) {
@@ -672,7 +771,7 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
672
771
  GET_CLIENT(self);
673
772
 
674
773
  /* Check if execution terminated while result was still being read. */
675
- if (!NIL_P(wrapper->active_thread)) {
774
+ if (!NIL_P(wrapper->active_fiber)) {
676
775
  if (CONNECTED(wrapper)) {
677
776
  /* Invalidate the MySQL socket to prevent further communication. */
678
777
  #ifndef _WIN32
@@ -687,24 +786,24 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
687
786
  }
688
787
  /* Skip mysql client check performed before command execution. */
689
788
  wrapper->client->status = MYSQL_STATUS_READY;
690
- wrapper->active_thread = Qnil;
789
+ wrapper->active_fiber = Qnil;
691
790
  }
692
791
 
693
792
  return Qnil;
694
793
  }
695
794
 
696
- void rb_mysql_client_set_active_thread(VALUE self) {
697
- VALUE thread_current = rb_thread_current();
795
+ static void rb_mysql_client_set_active_fiber(VALUE self) {
796
+ VALUE fiber_current = rb_fiber_current();
698
797
  GET_CLIENT(self);
699
798
 
700
799
  // see if this connection is still waiting on a result from a previous query
701
- if (NIL_P(wrapper->active_thread)) {
800
+ if (NIL_P(wrapper->active_fiber)) {
702
801
  // mark this connection active
703
- wrapper->active_thread = thread_current;
704
- } else if (wrapper->active_thread == thread_current) {
802
+ wrapper->active_fiber = fiber_current;
803
+ } else if (wrapper->active_fiber == fiber_current) {
705
804
  rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
706
805
  } else {
707
- VALUE inspect = rb_inspect(wrapper->active_thread);
806
+ VALUE inspect = rb_inspect(wrapper->active_fiber);
708
807
  const char *thr = StringValueCStr(inspect);
709
808
 
710
809
  rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
@@ -747,7 +846,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
747
846
  * Query the database with +sql+, with optional +options+. For the possible
748
847
  * options, see default_query_options on the Mysql2::Client class.
749
848
  */
750
- static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
849
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
751
850
  #ifndef _WIN32
752
851
  struct async_query_args async_args;
753
852
  #endif
@@ -759,23 +858,20 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
759
858
 
760
859
  (void)RB_GC_GUARD(current);
761
860
  Check_Type(current, T_HASH);
762
- rb_iv_set(self, "@current_query_options", current);
861
+ rb_ivar_set(self, intern_current_query_options, current);
763
862
 
764
863
  Check_Type(sql, T_STRING);
765
- #ifdef HAVE_RUBY_ENCODING_H
766
864
  /* ensure the string is in the encoding the connection is expecting */
767
865
  args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
768
- #else
769
- args.sql = sql;
770
- #endif
771
866
  args.sql_ptr = RSTRING_PTR(args.sql);
772
867
  args.sql_len = RSTRING_LEN(args.sql);
773
868
  args.wrapper = wrapper;
774
869
 
775
- rb_mysql_client_set_active_thread(self);
870
+ rb_mysql_client_set_active_fiber(self);
776
871
 
777
872
  #ifndef _WIN32
778
873
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
874
+ (void)RB_GC_GUARD(sql);
779
875
 
780
876
  if (rb_hash_aref(current, sym_async) == Qtrue) {
781
877
  return Qnil;
@@ -788,7 +884,8 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
788
884
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
789
885
  }
790
886
  #else
791
- do_send_query(&args);
887
+ do_send_query((VALUE)&args);
888
+ (void)RB_GC_GUARD(sql);
792
889
 
793
890
  /* this will just block until the result is ready */
794
891
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
@@ -804,20 +901,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
804
901
  unsigned char *newStr;
805
902
  VALUE rb_str;
806
903
  unsigned long newLen, oldLen;
807
- #ifdef HAVE_RUBY_ENCODING_H
808
904
  rb_encoding *default_internal_enc;
809
905
  rb_encoding *conn_enc;
810
- #endif
811
906
  GET_CLIENT(self);
812
907
 
813
908
  REQUIRE_CONNECTED(wrapper);
814
909
  Check_Type(str, T_STRING);
815
- #ifdef HAVE_RUBY_ENCODING_H
816
910
  default_internal_enc = rb_default_internal_encoding();
817
911
  conn_enc = rb_to_encoding(wrapper->encoding);
818
912
  /* ensure the string is in the encoding the connection is expecting */
819
913
  str = rb_str_export_to_enc(str, conn_enc);
820
- #endif
821
914
 
822
915
  oldLen = RSTRING_LEN(str);
823
916
  newStr = xmalloc(oldLen*2+1);
@@ -825,21 +918,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
825
918
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
826
919
  if (newLen == oldLen) {
827
920
  /* no need to return a new ruby string if nothing changed */
828
- #ifdef HAVE_RUBY_ENCODING_H
829
921
  if (default_internal_enc) {
830
922
  str = rb_str_export_to_enc(str, default_internal_enc);
831
923
  }
832
- #endif
833
924
  xfree(newStr);
834
925
  return str;
835
926
  } else {
836
927
  rb_str = rb_str_new((const char*)newStr, newLen);
837
- #ifdef HAVE_RUBY_ENCODING_H
838
928
  rb_enc_associate(rb_str, conn_enc);
839
929
  if (default_internal_enc) {
840
930
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
841
931
  }
842
- #endif
843
932
  xfree(newStr);
844
933
  return rb_str;
845
934
  }
@@ -850,7 +939,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
850
939
  const void *retval = NULL;
851
940
  unsigned int intval = 0;
852
941
  const char * charval = NULL;
853
- bool boolval;
942
+ my_bool boolval;
854
943
 
855
944
  GET_CLIENT(self);
856
945
 
@@ -907,6 +996,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
907
996
  retval = charval;
908
997
  break;
909
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
+
910
1006
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
911
1007
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
912
1008
  boolval = (value == Qfalse ? 0 : 1);
@@ -950,10 +1046,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
950
1046
  version = rb_str_new2(mysql_get_client_info());
951
1047
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
952
1048
 
953
- #ifdef HAVE_RUBY_ENCODING_H
954
1049
  rb_enc_associate(version, rb_usascii_encoding());
955
1050
  rb_enc_associate(header_version, rb_usascii_encoding());
956
- #endif
957
1051
 
958
1052
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
959
1053
  rb_hash_aset(version_info, sym_version, version);
@@ -969,27 +1063,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
969
1063
  */
970
1064
  static VALUE rb_mysql_client_server_info(VALUE self) {
971
1065
  VALUE version, server_info;
972
- #ifdef HAVE_RUBY_ENCODING_H
973
1066
  rb_encoding *default_internal_enc;
974
1067
  rb_encoding *conn_enc;
975
- #endif
976
1068
  GET_CLIENT(self);
977
1069
 
978
1070
  REQUIRE_CONNECTED(wrapper);
979
- #ifdef HAVE_RUBY_ENCODING_H
980
1071
  default_internal_enc = rb_default_internal_encoding();
981
1072
  conn_enc = rb_to_encoding(wrapper->encoding);
982
- #endif
983
1073
 
984
1074
  version = rb_hash_new();
985
1075
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
986
1076
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
987
- #ifdef HAVE_RUBY_ENCODING_H
988
1077
  rb_enc_associate(server_info, conn_enc);
989
1078
  if (default_internal_enc) {
990
1079
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
991
1080
  }
992
- #endif
993
1081
  rb_hash_aset(version, sym_version, server_info);
994
1082
  return version;
995
1083
  }
@@ -1023,6 +1111,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
1023
1111
  return ULL2NUM(mysql_insert_id(wrapper->client));
1024
1112
  }
1025
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
+
1026
1144
  /* call-seq:
1027
1145
  * client.affected_rows
1028
1146
  *
@@ -1110,6 +1228,23 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1110
1228
  }
1111
1229
  }
1112
1230
 
1231
+ /* call-seq:
1232
+ * client.set_server_option(value)
1233
+ *
1234
+ * Enables or disables an option for the connection.
1235
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1236
+ * for more information.
1237
+ */
1238
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1239
+ GET_CLIENT(self);
1240
+
1241
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1242
+ return Qtrue;
1243
+ } else {
1244
+ return Qfalse;
1245
+ }
1246
+ }
1247
+
1113
1248
  /* call-seq:
1114
1249
  * client.more_results?
1115
1250
  *
@@ -1168,7 +1303,8 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1168
1303
  return Qnil;
1169
1304
  }
1170
1305
 
1171
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1306
+ // Duplicate the options hash and put the copy in the Result object
1307
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1172
1308
  (void)RB_GC_GUARD(current);
1173
1309
  Check_Type(current, T_HASH);
1174
1310
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -1176,7 +1312,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1176
1312
  return resultObj;
1177
1313
  }
1178
1314
 
1179
- #ifdef HAVE_RUBY_ENCODING_H
1180
1315
  /* call-seq:
1181
1316
  * client.encoding
1182
1317
  *
@@ -1186,7 +1321,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1186
1321
  GET_CLIENT(self);
1187
1322
  return wrapper->encoding;
1188
1323
  }
1189
- #endif
1190
1324
 
1191
1325
  /* call-seq:
1192
1326
  * client.automatic_close?
@@ -1256,7 +1390,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1256
1390
  /* Set the instance variable here even though _mysql_client_options
1257
1391
  might not succeed, because the timeout is used in other ways
1258
1392
  elsewhere */
1259
- rb_iv_set(self, "@read_timeout", value);
1393
+ rb_ivar_set(self, intern_read_timeout, value);
1260
1394
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1261
1395
  }
1262
1396
 
@@ -1272,17 +1406,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1272
1406
 
1273
1407
  static VALUE set_charset_name(VALUE self, VALUE value) {
1274
1408
  char *charset_name;
1275
- #ifdef HAVE_RUBY_ENCODING_H
1276
1409
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1277
1410
  rb_encoding *enc;
1278
1411
  VALUE rb_enc;
1279
- #endif
1280
1412
  GET_CLIENT(self);
1281
1413
 
1282
1414
  Check_Type(value, T_STRING);
1283
1415
  charset_name = RSTRING_PTR(value);
1284
1416
 
1285
- #ifdef HAVE_RUBY_ENCODING_H
1286
1417
  mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1287
1418
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1288
1419
  VALUE inspect = rb_inspect(value);
@@ -1292,7 +1423,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1292
1423
  rb_enc = rb_enc_from_encoding(enc);
1293
1424
  wrapper->encoding = rb_enc;
1294
1425
  }
1295
- #endif
1296
1426
 
1297
1427
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1298
1428
  /* TODO: warning - unable to set charset */
@@ -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
 
@@ -1416,6 +1574,7 @@ void init_mysql2_client() {
1416
1574
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1417
1575
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1418
1576
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1577
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1419
1578
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1420
1579
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1421
1580
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
@@ -1425,9 +1584,8 @@ void init_mysql2_client() {
1425
1584
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1426
1585
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1427
1586
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1428
- #ifdef HAVE_RUBY_ENCODING_H
1429
1587
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1430
- #endif
1588
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1431
1589
 
1432
1590
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1433
1591
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1438,12 +1596,13 @@ void init_mysql2_client() {
1438
1596
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1439
1597
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1440
1598
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1599
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1441
1600
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1442
1601
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1443
1602
  rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1444
1603
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1445
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1446
- rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
1604
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1605
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1447
1606
 
1448
1607
  sym_id = ID2SYM(rb_intern("id"));
1449
1608
  sym_version = ID2SYM(rb_intern("version"));
@@ -1454,10 +1613,16 @@ void init_mysql2_client() {
1454
1613
  sym_array = ID2SYM(rb_intern("array"));
1455
1614
  sym_stream = ID2SYM(rb_intern("stream"));
1456
1615
 
1616
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1617
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1618
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1619
+
1457
1620
  intern_brackets = rb_intern("[]");
1458
1621
  intern_merge = rb_intern("merge");
1459
1622
  intern_merge_bang = rb_intern("merge!");
1460
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");
1461
1626
 
1462
1627
  #ifdef CLIENT_LONG_PASSWORD
1463
1628
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
@@ -1543,6 +1708,16 @@ void init_mysql2_client() {
1543
1708
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1544
1709
  #endif
1545
1710
 
1711
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1712
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1713
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1714
+ #endif
1715
+
1716
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1717
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1718
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1719
+ #endif
1720
+
1546
1721
  #ifdef CLIENT_MULTI_STATEMENTS
1547
1722
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1548
1723
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1573,16 +1748,42 @@ void init_mysql2_client() {
1573
1748
  LONG2NUM(CLIENT_BASIC_FLAGS));
1574
1749
  #endif
1575
1750
 
1576
- #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1751
+ #ifdef CLIENT_CONNECT_ATTRS
1752
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1753
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1754
+ #else
1755
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1756
+ * but we're using it in our default connection flags. */
1757
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1758
+ INT2NUM(0));
1759
+ #endif
1760
+
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
1577
1773
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1578
1774
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1579
1775
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1580
1776
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1581
1777
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1582
- #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
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
1583
1783
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1584
1784
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1585
1785
  #endif
1786
+ #endif
1586
1787
 
1587
1788
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1588
1789
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
@@ -1600,3 +1801,29 @@ void init_mysql2_client() {
1600
1801
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1601
1802
  #endif
1602
1803
  }
1804
+
1805
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1806
+
1807
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1808
+ VALUE server_flags = rb_hash_new();
1809
+
1810
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1811
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1812
+ #else
1813
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1814
+ #endif
1815
+
1816
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1817
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1818
+ #else
1819
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1820
+ #endif
1821
+
1822
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1823
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1824
+ #else
1825
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1826
+ #endif
1827
+
1828
+ rb_iv_set(result, "@server_flags", server_flags);
1829
+ }