mysql2 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mysql2/client.c CHANGED
@@ -15,24 +15,31 @@
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 VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
20
21
  static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
22
 
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
27
-
28
23
  #define REQUIRE_INITIALIZED(wrapper) \
29
24
  if (!wrapper->initialized) { \
30
25
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
26
  }
32
27
 
28
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
29
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
30
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
31
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
32
+ #endif
33
+
34
+ #define REQUIRE_CONNECTED(wrapper) \
35
+ REQUIRE_INITIALIZED(wrapper) \
36
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
37
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
38
+ }
39
+
33
40
  #define REQUIRE_NOT_CONNECTED(wrapper) \
34
41
  REQUIRE_INITIALIZED(wrapper) \
35
- if (wrapper->connected) { \
42
+ if (CONNECTED(wrapper)) { \
36
43
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
37
44
  }
38
45
 
@@ -41,12 +48,24 @@ VALUE rb_hash_dup(VALUE other) {
41
48
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
42
49
  * linking against the server itself
43
50
  */
44
- #ifdef LIBMYSQL_VERSION
51
+ #if defined(MARIADB_CLIENT_VERSION_STR)
52
+ #define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
53
+ #elif defined(LIBMYSQL_VERSION)
45
54
  #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
46
55
  #else
47
56
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
48
57
  #endif
49
58
 
59
+ /*
60
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
61
+ */
62
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
63
+ #define SSL_MODE_DISABLED 1
64
+ #define SSL_MODE_REQUIRED 3
65
+ #define HAVE_CONST_SSL_MODE_DISABLED
66
+ #define HAVE_CONST_SSL_MODE_REQUIRED
67
+ #endif
68
+
50
69
  /*
51
70
  * used to pass all arguments to mysql_real_connect while inside
52
71
  * rb_thread_call_without_gvl
@@ -83,6 +102,42 @@ struct nogvl_select_db_args {
83
102
  char *db;
84
103
  };
85
104
 
105
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
106
+ unsigned long version = mysql_get_client_version();
107
+
108
+ if (version < 50703) {
109
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
110
+ return Qnil;
111
+ }
112
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
113
+ GET_CLIENT(self);
114
+ int val = NUM2INT( setting );
115
+ if (version >= 50703 && version < 50711) {
116
+ 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 );
119
+ 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
+ }
124
+ }
125
+ #endif
126
+ #ifdef FULL_SSL_MODE_SUPPORT
127
+ GET_CLIENT(self);
128
+ int val = NUM2INT( setting );
129
+
130
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
131
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
132
+ }
133
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
134
+
135
+ return INT2NUM(result);
136
+ #endif
137
+ #ifdef NO_SSL_MODE_SUPPORT
138
+ return Qnil;
139
+ #endif
140
+ }
86
141
  /*
87
142
  * non-blocking mysql_*() functions that we won't be wrapping since
88
143
  * they do not appear to hit the network nor issue any interruptible
@@ -118,10 +173,8 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
118
173
  VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
119
174
  VALUE e;
120
175
 
121
- #ifdef HAVE_RUBY_ENCODING_H
122
176
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
123
177
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
124
- #endif
125
178
 
126
179
  e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
127
180
  rb_error_msg,
@@ -211,11 +264,10 @@ static VALUE invalidate_fd(int clientfd)
211
264
  static void *nogvl_close(void *ptr) {
212
265
  mysql_client_wrapper *wrapper = ptr;
213
266
 
214
- if (wrapper->client) {
267
+ if (!wrapper->closed) {
215
268
  mysql_close(wrapper->client);
216
- xfree(wrapper->client);
217
- wrapper->client = NULL;
218
- wrapper->connected = 0;
269
+ wrapper->closed = 1;
270
+ wrapper->reconnect_enabled = 0;
219
271
  wrapper->active_thread = Qnil;
220
272
  }
221
273
 
@@ -234,7 +286,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
234
286
 
235
287
  if (wrapper->refcount == 0) {
236
288
  #ifndef _WIN32
237
- if (wrapper->connected && !wrapper->automatic_close) {
289
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
238
290
  /* The client is being garbage collected while connected. Prevent
239
291
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
240
292
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -246,10 +298,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
246
298
  fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
247
299
  close(wrapper->client->net.fd);
248
300
  }
301
+ wrapper->client->net.fd = -1;
249
302
  }
250
303
  #endif
251
304
 
252
305
  nogvl_close(wrapper);
306
+ xfree(wrapper->client);
253
307
  xfree(wrapper);
254
308
  }
255
309
  }
@@ -264,9 +318,9 @@ static VALUE allocate(VALUE klass) {
264
318
  wrapper->server_version = 0;
265
319
  wrapper->reconnect_enabled = 0;
266
320
  wrapper->connect_timeout = 0;
267
- wrapper->connected = 0; /* means that a database connection is open */
268
321
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
269
322
  wrapper->refcount = 1;
323
+ wrapper->closed = 0;
270
324
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
271
325
 
272
326
  return obj;
@@ -296,9 +350,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
296
350
  return str;
297
351
  } else {
298
352
  rb_str = rb_str_new((const char*)newStr, newLen);
299
- #ifdef HAVE_RUBY_ENCODING_H
300
353
  rb_enc_copy(rb_str, str);
301
- #endif
302
354
  xfree(newStr);
303
355
  return rb_str;
304
356
  }
@@ -325,9 +377,7 @@ static VALUE rb_mysql_info(VALUE self) {
325
377
  }
326
378
 
327
379
  rb_str = rb_str_new2(info);
328
- #ifdef HAVE_RUBY_ENCODING_H
329
380
  rb_enc_associate(rb_str, rb_utf8_encoding());
330
- #endif
331
381
 
332
382
  return rb_str;
333
383
  }
@@ -345,14 +395,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
345
395
  }
346
396
 
347
397
  rb_str = rb_str_new2(cipher);
348
- #ifdef HAVE_RUBY_ENCODING_H
349
398
  rb_enc_associate(rb_str, rb_utf8_encoding());
350
- #endif
351
399
 
352
400
  return rb_str;
353
401
  }
354
402
 
355
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
403
+ #ifdef CLIENT_CONNECT_ATTRS
404
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
405
+ {
406
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
407
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
408
+ key = rb_str_export_to_enc(key, enc);
409
+ value = rb_str_export_to_enc(value, enc);
410
+
411
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
412
+ return ST_CONTINUE;
413
+ }
414
+ #endif
415
+
416
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
356
417
  struct nogvl_connect_args args;
357
418
  time_t start_time, end_time, elapsed_time, connect_timeout;
358
419
  VALUE rv;
@@ -367,6 +428,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
367
428
  args.mysql = wrapper->client;
368
429
  args.client_flag = NUM2ULONG(flags);
369
430
 
431
+ #ifdef CLIENT_CONNECT_ATTRS
432
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
433
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
434
+ #endif
435
+
370
436
  if (wrapper->connect_timeout)
371
437
  time(&start_time);
372
438
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -397,7 +463,6 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
397
463
  }
398
464
 
399
465
  wrapper->server_version = mysql_get_server_version(wrapper->client);
400
- wrapper->connected = 1;
401
466
  return self;
402
467
  }
403
468
 
@@ -412,13 +477,23 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
412
477
  static VALUE rb_mysql_client_close(VALUE self) {
413
478
  GET_CLIENT(self);
414
479
 
415
- if (wrapper->connected) {
480
+ if (wrapper->client) {
416
481
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
417
482
  }
418
483
 
419
484
  return Qnil;
420
485
  }
421
486
 
487
+ /* call-seq:
488
+ * client.closed?
489
+ *
490
+ * @return [Boolean]
491
+ */
492
+ static VALUE rb_mysql_client_closed(VALUE self) {
493
+ GET_CLIENT(self);
494
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
495
+ }
496
+
422
497
  /*
423
498
  * mysql_send_query is unlikely to block since most queries are small
424
499
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -520,11 +595,14 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
520
595
  return Qnil;
521
596
  }
522
597
 
598
+ // Duplicate the options hash and put the copy in the Result object
523
599
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
524
600
  (void)RB_GC_GUARD(current);
525
601
  Check_Type(current, T_HASH);
526
602
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
527
603
 
604
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
605
+
528
606
  return resultObj;
529
607
  }
530
608
 
@@ -538,14 +616,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
538
616
  GET_CLIENT(self);
539
617
 
540
618
  wrapper->active_thread = Qnil;
541
- wrapper->connected = 0;
542
619
 
543
620
  /* Invalidate the MySQL socket to prevent further communication.
544
621
  * The GC will come along later and call mysql_close to free it.
545
622
  */
546
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
547
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
548
- close(wrapper->client->net.fd);
623
+ if (CONNECTED(wrapper)) {
624
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
625
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
626
+ close(wrapper->client->net.fd);
627
+ }
628
+ wrapper->client->net.fd = -1;
549
629
  }
550
630
 
551
631
  rb_exc_raise(error);
@@ -580,7 +660,7 @@ static VALUE do_query(void *args) {
580
660
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
581
661
 
582
662
  if (retval == 0) {
583
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
663
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
584
664
  }
585
665
 
586
666
  if (retval < 0) {
@@ -594,26 +674,32 @@ static VALUE do_query(void *args) {
594
674
 
595
675
  return Qnil;
596
676
  }
597
- #else
598
- static VALUE finish_and_mark_inactive(void *args) {
599
- VALUE self = args;
600
- MYSQL_RES *result;
677
+ #endif
601
678
 
679
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
602
680
  GET_CLIENT(self);
603
681
 
682
+ /* Check if execution terminated while result was still being read. */
604
683
  if (!NIL_P(wrapper->active_thread)) {
605
- /* if we got here, the result hasn't been read off the wire yet
606
- so lets do that and then throw it away because we have no way
607
- of getting it back up to the caller from here */
608
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
609
- mysql_free_result(result);
610
-
684
+ if (CONNECTED(wrapper)) {
685
+ /* Invalidate the MySQL socket to prevent further communication. */
686
+ #ifndef _WIN32
687
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
688
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
689
+ close(wrapper->client->net.fd);
690
+ }
691
+ #else
692
+ close(wrapper->client->net.fd);
693
+ #endif
694
+ wrapper->client->net.fd = -1;
695
+ }
696
+ /* Skip mysql client check performed before command execution. */
697
+ wrapper->client->status = MYSQL_STATUS_READY;
611
698
  wrapper->active_thread = Qnil;
612
699
  }
613
700
 
614
701
  return Qnil;
615
702
  }
616
- #endif
617
703
 
618
704
  void rb_mysql_client_set_active_thread(VALUE self) {
619
705
  VALUE thread_current = rb_thread_current();
@@ -669,7 +755,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
669
755
  * Query the database with +sql+, with optional +options+. For the possible
670
756
  * options, see default_query_options on the Mysql2::Client class.
671
757
  */
672
- static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
758
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
673
759
  #ifndef _WIN32
674
760
  struct async_query_args async_args;
675
761
  #endif
@@ -684,12 +770,8 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
684
770
  rb_iv_set(self, "@current_query_options", current);
685
771
 
686
772
  Check_Type(sql, T_STRING);
687
- #ifdef HAVE_RUBY_ENCODING_H
688
773
  /* ensure the string is in the encoding the connection is expecting */
689
774
  args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
690
- #else
691
- args.sql = sql;
692
- #endif
693
775
  args.sql_ptr = RSTRING_PTR(args.sql);
694
776
  args.sql_len = RSTRING_LEN(args.sql);
695
777
  args.wrapper = wrapper;
@@ -707,13 +789,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
707
789
 
708
790
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
709
791
 
710
- return rb_mysql_client_async_result(self);
792
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
711
793
  }
712
794
  #else
713
795
  do_send_query(&args);
714
796
 
715
797
  /* this will just block until the result is ready */
716
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
798
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
717
799
  #endif
718
800
  }
719
801
 
@@ -726,20 +808,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
726
808
  unsigned char *newStr;
727
809
  VALUE rb_str;
728
810
  unsigned long newLen, oldLen;
729
- #ifdef HAVE_RUBY_ENCODING_H
730
811
  rb_encoding *default_internal_enc;
731
812
  rb_encoding *conn_enc;
732
- #endif
733
813
  GET_CLIENT(self);
734
814
 
735
815
  REQUIRE_CONNECTED(wrapper);
736
816
  Check_Type(str, T_STRING);
737
- #ifdef HAVE_RUBY_ENCODING_H
738
817
  default_internal_enc = rb_default_internal_encoding();
739
818
  conn_enc = rb_to_encoding(wrapper->encoding);
740
819
  /* ensure the string is in the encoding the connection is expecting */
741
820
  str = rb_str_export_to_enc(str, conn_enc);
742
- #endif
743
821
 
744
822
  oldLen = RSTRING_LEN(str);
745
823
  newStr = xmalloc(oldLen*2+1);
@@ -747,21 +825,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
747
825
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
748
826
  if (newLen == oldLen) {
749
827
  /* no need to return a new ruby string if nothing changed */
750
- #ifdef HAVE_RUBY_ENCODING_H
751
828
  if (default_internal_enc) {
752
829
  str = rb_str_export_to_enc(str, default_internal_enc);
753
830
  }
754
- #endif
755
831
  xfree(newStr);
756
832
  return str;
757
833
  } else {
758
834
  rb_str = rb_str_new((const char*)newStr, newLen);
759
- #ifdef HAVE_RUBY_ENCODING_H
760
835
  rb_enc_associate(rb_str, conn_enc);
761
836
  if (default_internal_enc) {
762
837
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
763
838
  }
764
- #endif
765
839
  xfree(newStr);
766
840
  return rb_str;
767
841
  }
@@ -807,10 +881,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
807
881
  retval = &boolval;
808
882
  break;
809
883
 
884
+ #ifdef MYSQL_SECURE_AUTH
810
885
  case MYSQL_SECURE_AUTH:
811
886
  boolval = (value == Qfalse ? 0 : 1);
812
887
  retval = &boolval;
813
888
  break;
889
+ #endif
814
890
 
815
891
  case MYSQL_READ_DEFAULT_FILE:
816
892
  charval = (const char *)StringValueCStr(value);
@@ -827,6 +903,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
827
903
  retval = charval;
828
904
  break;
829
905
 
906
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
907
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
908
+ boolval = (value == Qfalse ? 0 : 1);
909
+ retval = &boolval;
910
+ break;
911
+ #endif
912
+
830
913
  default:
831
914
  return Qfalse;
832
915
  }
@@ -863,10 +946,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
863
946
  version = rb_str_new2(mysql_get_client_info());
864
947
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
865
948
 
866
- #ifdef HAVE_RUBY_ENCODING_H
867
949
  rb_enc_associate(version, rb_usascii_encoding());
868
950
  rb_enc_associate(header_version, rb_usascii_encoding());
869
- #endif
870
951
 
871
952
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
872
953
  rb_hash_aset(version_info, sym_version, version);
@@ -882,27 +963,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
882
963
  */
883
964
  static VALUE rb_mysql_client_server_info(VALUE self) {
884
965
  VALUE version, server_info;
885
- #ifdef HAVE_RUBY_ENCODING_H
886
966
  rb_encoding *default_internal_enc;
887
967
  rb_encoding *conn_enc;
888
- #endif
889
968
  GET_CLIENT(self);
890
969
 
891
970
  REQUIRE_CONNECTED(wrapper);
892
- #ifdef HAVE_RUBY_ENCODING_H
893
971
  default_internal_enc = rb_default_internal_encoding();
894
972
  conn_enc = rb_to_encoding(wrapper->encoding);
895
- #endif
896
973
 
897
974
  version = rb_hash_new();
898
975
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
899
976
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
900
- #ifdef HAVE_RUBY_ENCODING_H
901
977
  rb_enc_associate(server_info, conn_enc);
902
978
  if (default_internal_enc) {
903
979
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
904
980
  }
905
- #endif
906
981
  rb_hash_aset(version, sym_version, server_info);
907
982
  return version;
908
983
  }
@@ -1016,13 +1091,30 @@ static void *nogvl_ping(void *ptr) {
1016
1091
  static VALUE rb_mysql_client_ping(VALUE self) {
1017
1092
  GET_CLIENT(self);
1018
1093
 
1019
- if (!wrapper->connected) {
1094
+ if (!CONNECTED(wrapper)) {
1020
1095
  return Qfalse;
1021
1096
  } else {
1022
1097
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1023
1098
  }
1024
1099
  }
1025
1100
 
1101
+ /* call-seq:
1102
+ * client.set_server_option(value)
1103
+ *
1104
+ * Enables or disables an option for the connection.
1105
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1106
+ * for more information.
1107
+ */
1108
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1109
+ GET_CLIENT(self);
1110
+
1111
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1112
+ return Qtrue;
1113
+ } else {
1114
+ return Qfalse;
1115
+ }
1116
+ }
1117
+
1026
1118
  /* call-seq:
1027
1119
  * client.more_results?
1028
1120
  *
@@ -1081,6 +1173,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1081
1173
  return Qnil;
1082
1174
  }
1083
1175
 
1176
+ // Duplicate the options hash and put the copy in the Result object
1084
1177
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1085
1178
  (void)RB_GC_GUARD(current);
1086
1179
  Check_Type(current, T_HASH);
@@ -1089,7 +1182,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1089
1182
  return resultObj;
1090
1183
  }
1091
1184
 
1092
- #ifdef HAVE_RUBY_ENCODING_H
1093
1185
  /* call-seq:
1094
1186
  * client.encoding
1095
1187
  *
@@ -1099,7 +1191,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1099
1191
  GET_CLIENT(self);
1100
1192
  return wrapper->encoding;
1101
1193
  }
1102
- #endif
1103
1194
 
1104
1195
  /* call-seq:
1105
1196
  * client.automatic_close?
@@ -1185,16 +1276,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1185
1276
 
1186
1277
  static VALUE set_charset_name(VALUE self, VALUE value) {
1187
1278
  char *charset_name;
1188
- #ifdef HAVE_RUBY_ENCODING_H
1189
1279
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1190
1280
  rb_encoding *enc;
1191
1281
  VALUE rb_enc;
1192
- #endif
1193
1282
  GET_CLIENT(self);
1194
1283
 
1284
+ Check_Type(value, T_STRING);
1195
1285
  charset_name = RSTRING_PTR(value);
1196
1286
 
1197
- #ifdef HAVE_RUBY_ENCODING_H
1198
1287
  mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1199
1288
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1200
1289
  VALUE inspect = rb_inspect(value);
@@ -1204,7 +1293,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1204
1293
  rb_enc = rb_enc_from_encoding(enc);
1205
1294
  wrapper->encoding = rb_enc;
1206
1295
  }
1207
- #endif
1208
1296
 
1209
1297
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1210
1298
  /* TODO: warning - unable to set charset */
@@ -1228,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1228
1316
  }
1229
1317
 
1230
1318
  static VALUE set_secure_auth(VALUE self, VALUE value) {
1319
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1320
+ #ifdef MYSQL_SECURE_AUTH
1231
1321
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1322
+ #else
1323
+ return Qfalse;
1324
+ #endif
1232
1325
  }
1233
1326
 
1234
1327
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1243,6 +1336,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1243
1336
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1244
1337
  }
1245
1338
 
1339
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1342
+ #else
1343
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1344
+ #endif
1345
+ }
1346
+
1246
1347
  static VALUE initialize_ext(VALUE self) {
1247
1348
  GET_CLIENT(self);
1248
1349
 
@@ -1303,6 +1404,7 @@ void init_mysql2_client() {
1303
1404
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1304
1405
 
1305
1406
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1407
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1306
1408
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1307
1409
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1308
1410
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1314,6 +1416,7 @@ void init_mysql2_client() {
1314
1416
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1315
1417
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1316
1418
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1419
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1317
1420
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1318
1421
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1319
1422
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
@@ -1323,9 +1426,7 @@ void init_mysql2_client() {
1323
1426
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1324
1427
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1325
1428
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1326
- #ifdef HAVE_RUBY_ENCODING_H
1327
1429
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1328
- #endif
1329
1430
 
1330
1431
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1331
1432
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1337,9 +1438,11 @@ void init_mysql2_client() {
1337
1438
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1338
1439
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1339
1440
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1441
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1442
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1340
1443
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1341
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1342
- rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
1444
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1445
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1343
1446
 
1344
1447
  sym_id = ID2SYM(rb_intern("id"));
1345
1448
  sym_version = ID2SYM(rb_intern("version"));
@@ -1350,6 +1453,10 @@ void init_mysql2_client() {
1350
1453
  sym_array = ID2SYM(rb_intern("array"));
1351
1454
  sym_stream = ID2SYM(rb_intern("stream"));
1352
1455
 
1456
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1457
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1458
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1459
+
1353
1460
  intern_brackets = rb_intern("[]");
1354
1461
  intern_merge = rb_intern("merge");
1355
1462
  intern_merge_bang = rb_intern("merge!");
@@ -1358,6 +1465,10 @@ void init_mysql2_client() {
1358
1465
  #ifdef CLIENT_LONG_PASSWORD
1359
1466
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1360
1467
  LONG2NUM(CLIENT_LONG_PASSWORD));
1468
+ #else
1469
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1470
+ * but we're using it in our default connection flags. */
1471
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1361
1472
  #endif
1362
1473
 
1363
1474
  #ifdef CLIENT_FOUND_ROWS
@@ -1435,6 +1546,16 @@ void init_mysql2_client() {
1435
1546
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1436
1547
  #endif
1437
1548
 
1549
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1550
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1551
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1552
+ #endif
1553
+
1554
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1555
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1556
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1557
+ #endif
1558
+
1438
1559
  #ifdef CLIENT_MULTI_STATEMENTS
1439
1560
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1440
1561
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1464,4 +1585,67 @@ void init_mysql2_client() {
1464
1585
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1465
1586
  LONG2NUM(CLIENT_BASIC_FLAGS));
1466
1587
  #endif
1588
+
1589
+ #ifdef CLIENT_CONNECT_ATTRS
1590
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1591
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1592
+ #else
1593
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1594
+ * but we're using it in our default connection flags. */
1595
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1596
+ INT2NUM(0));
1597
+ #endif
1598
+
1599
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1600
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1601
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1602
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1603
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1604
+ 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
1606
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1607
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1608
+ #endif
1609
+
1610
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1611
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1612
+ #endif
1613
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1614
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1615
+ #endif
1616
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1617
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1618
+ #endif
1619
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1620
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1621
+ #endif
1622
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1623
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1624
+ #endif
1625
+ }
1626
+
1627
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1628
+
1629
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1630
+ VALUE server_flags = rb_hash_new();
1631
+
1632
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1633
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1634
+ #else
1635
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1636
+ #endif
1637
+
1638
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1639
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1640
+ #else
1641
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1642
+ #endif
1643
+
1644
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1645
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1646
+ #else
1647
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1648
+ #endif
1649
+
1650
+ rb_iv_set(result, "@server_flags", server_flags);
1467
1651
  }