mysql2 0.4.10 → 0.5.6

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