mysql2 0.4.4 → 0.5.0

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.
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
  }