mysql2 0.4.10 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +98 -38
  3. data/ext/mysql2/client.c +223 -76
  4. data/ext/mysql2/client.h +1 -39
  5. data/ext/mysql2/extconf.rb +46 -26
  6. data/ext/mysql2/mysql2_ext.c +8 -2
  7. data/ext/mysql2/mysql2_ext.h +8 -4
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
  9. data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
  10. data/ext/mysql2/result.c +242 -86
  11. data/ext/mysql2/result.h +3 -3
  12. data/ext/mysql2/statement.c +90 -73
  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 +51 -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 +18 -15
  22. data/support/3A79BD29.asc +49 -0
  23. data/support/5072E1F5.asc +5 -5
  24. data/support/mysql_enc_to_ruby.rb +8 -3
  25. data/support/ruby_enc_to_mysql.rb +7 -5
  26. metadata +14 -58
  27. data/examples/eventmachine.rb +0 -21
  28. data/examples/threaded.rb +0 -18
  29. data/spec/configuration.yml.example +0 -11
  30. data/spec/em/em_spec.rb +0 -136
  31. data/spec/my.cnf.example +0 -9
  32. data/spec/mysql2/client_spec.rb +0 -1039
  33. data/spec/mysql2/error_spec.rb +0 -82
  34. data/spec/mysql2/result_spec.rb +0 -545
  35. data/spec/mysql2/statement_spec.rb +0 -776
  36. data/spec/rcov.opts +0 -3
  37. data/spec/spec_helper.rb +0 -108
  38. data/spec/ssl/ca-cert.pem +0 -17
  39. data/spec/ssl/ca-key.pem +0 -27
  40. data/spec/ssl/ca.cnf +0 -22
  41. data/spec/ssl/cert.cnf +0 -22
  42. data/spec/ssl/client-cert.pem +0 -17
  43. data/spec/ssl/client-key.pem +0 -27
  44. data/spec/ssl/client-req.pem +0 -15
  45. data/spec/ssl/gen_certs.sh +0 -48
  46. data/spec/ssl/pkcs8-client-key.pem +0 -28
  47. data/spec/ssl/pkcs8-server-key.pem +0 -28
  48. data/spec/ssl/server-cert.pem +0 -17
  49. data/spec/ssl/server-key.pem +0 -27
  50. data/spec/ssl/server-req.pem +0 -15
  51. 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
@@ -114,18 +125,30 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
114
125
  rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
115
126
  return Qnil;
116
127
  }
117
- #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
128
+ #if defined(HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT) || defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE)
118
129
  GET_CLIENT(self);
119
130
  int val = NUM2INT( setting );
120
- if (version >= 50703 && version < 50711) {
131
+ // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x, or MariaDB 10.x and later
132
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200) || version >= 100000) {
133
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
134
+ if (val == SSL_MODE_VERIFY_IDENTITY) {
135
+ my_bool b = 1;
136
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &b );
137
+ return INT2NUM(result);
138
+ }
139
+ #endif
140
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
121
141
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
122
- bool b = ( val == SSL_MODE_REQUIRED );
142
+ my_bool b = ( val == SSL_MODE_REQUIRED );
123
143
  int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
124
144
  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
145
  }
146
+ #endif
147
+ rb_warn( "Your mysql client library does not support ssl_mode %d.", val );
148
+ return Qnil;
149
+ } else {
150
+ rb_warn( "Your mysql client library does not support ssl_mode as expected." );
151
+ return Qnil;
129
152
  }
130
153
  #endif
131
154
  #ifdef FULL_SSL_MODE_SUPPORT
@@ -140,6 +163,7 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
140
163
  return INT2NUM(result);
141
164
  #endif
142
165
  #ifdef NO_SSL_MODE_SUPPORT
166
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
143
167
  return Qnil;
144
168
  #endif
145
169
  }
@@ -175,13 +199,11 @@ static void rb_mysql_client_mark(void * wrapper) {
175
199
 
176
200
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
177
201
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
178
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
202
+ VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
179
203
  VALUE e;
180
204
 
181
- #ifdef HAVE_RUBY_ENCODING_H
182
205
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
183
206
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
184
- #endif
185
207
 
186
208
  e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
187
209
  rb_error_msg,
@@ -271,7 +293,7 @@ static VALUE invalidate_fd(int clientfd)
271
293
  static void *nogvl_close(void *ptr) {
272
294
  mysql_client_wrapper *wrapper = ptr;
273
295
 
274
- if (!wrapper->closed) {
296
+ if (wrapper->initialized && !wrapper->closed) {
275
297
  mysql_close(wrapper->client);
276
298
  wrapper->closed = 1;
277
299
  wrapper->reconnect_enabled = 0;
@@ -325,9 +347,9 @@ static VALUE allocate(VALUE klass) {
325
347
  wrapper->server_version = 0;
326
348
  wrapper->reconnect_enabled = 0;
327
349
  wrapper->connect_timeout = 0;
328
- wrapper->initialized = 0; /* means that that the wrapper is initialized */
350
+ wrapper->initialized = 0; /* will be set true after calling mysql_init */
351
+ wrapper->closed = 1; /* will be set false after calling mysql_real_connect */
329
352
  wrapper->refcount = 1;
330
- wrapper->closed = 0;
331
353
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
332
354
 
333
355
  return obj;
@@ -357,9 +379,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
357
379
  return str;
358
380
  } else {
359
381
  rb_str = rb_str_new((const char*)newStr, newLen);
360
- #ifdef HAVE_RUBY_ENCODING_H
361
382
  rb_enc_copy(rb_str, str);
362
- #endif
363
383
  xfree(newStr);
364
384
  return rb_str;
365
385
  }
@@ -386,9 +406,7 @@ static VALUE rb_mysql_info(VALUE self) {
386
406
  }
387
407
 
388
408
  rb_str = rb_str_new2(info);
389
- #ifdef HAVE_RUBY_ENCODING_H
390
409
  rb_enc_associate(rb_str, rb_utf8_encoding());
391
- #endif
392
410
 
393
411
  return rb_str;
394
412
  }
@@ -406,14 +424,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
406
424
  }
407
425
 
408
426
  rb_str = rb_str_new2(cipher);
409
- #ifdef HAVE_RUBY_ENCODING_H
410
427
  rb_enc_associate(rb_str, rb_utf8_encoding());
411
- #endif
412
428
 
413
429
  return rb_str;
414
430
  }
415
431
 
416
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
432
+ #ifdef CLIENT_CONNECT_ATTRS
433
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
434
+ {
435
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
436
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
437
+ key = rb_str_export_to_enc(key, enc);
438
+ value = rb_str_export_to_enc(value, enc);
439
+
440
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
441
+ return ST_CONTINUE;
442
+ }
443
+ #endif
444
+
445
+ 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
446
  struct nogvl_connect_args args;
418
447
  time_t start_time, end_time, elapsed_time, connect_timeout;
419
448
  VALUE rv;
@@ -428,6 +457,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
428
457
  args.mysql = wrapper->client;
429
458
  args.client_flag = NUM2ULONG(flags);
430
459
 
460
+ #ifdef CLIENT_CONNECT_ATTRS
461
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
462
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
463
+ #endif
464
+
431
465
  if (wrapper->connect_timeout)
432
466
  time(&start_time);
433
467
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -457,6 +491,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
457
491
  rb_raise_mysql2_error(wrapper);
458
492
  }
459
493
 
494
+ wrapper->closed = 0;
460
495
  wrapper->server_version = mysql_get_server_version(wrapper->client);
461
496
  return self;
462
497
  }
@@ -503,10 +538,10 @@ static void *nogvl_send_query(void *ptr) {
503
538
  return (void*)(rv == 0 ? Qtrue : Qfalse);
504
539
  }
505
540
 
506
- static VALUE do_send_query(void *args) {
507
- struct nogvl_send_query_args *query_args = args;
541
+ static VALUE do_send_query(VALUE args) {
542
+ struct nogvl_send_query_args *query_args = (void *)args;
508
543
  mysql_client_wrapper *wrapper = query_args->wrapper;
509
- if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
544
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
510
545
  /* an error occurred, we're not active anymore */
511
546
  wrapper->active_thread = Qnil;
512
547
  rb_raise_mysql2_error(wrapper);
@@ -521,7 +556,7 @@ static VALUE do_send_query(void *args) {
521
556
  */
522
557
  static void *nogvl_read_query_result(void *ptr) {
523
558
  MYSQL * client = ptr;
524
- bool res = mysql_read_query_result(client);
559
+ my_bool res = mysql_read_query_result(client);
525
560
 
526
561
  return (void *)(res == 0 ? Qtrue : Qfalse);
527
562
  }
@@ -574,7 +609,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
574
609
  rb_raise_mysql2_error(wrapper);
575
610
  }
576
611
 
577
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
612
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
578
613
  if (is_streaming == Qtrue) {
579
614
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
580
615
  } else {
@@ -590,11 +625,14 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
590
625
  return Qnil;
591
626
  }
592
627
 
593
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
628
+ // Duplicate the options hash and put the copy in the Result object
629
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
594
630
  (void)RB_GC_GUARD(current);
595
631
  Check_Type(current, T_HASH);
596
632
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
597
633
 
634
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
635
+
598
636
  return resultObj;
599
637
  }
600
638
 
@@ -623,15 +661,15 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
623
661
  rb_exc_raise(error);
624
662
  }
625
663
 
626
- static VALUE do_query(void *args) {
627
- struct async_query_args *async_args = args;
664
+ static VALUE do_query(VALUE args) {
665
+ struct async_query_args *async_args = (void *)args;
628
666
  struct timeval tv;
629
667
  struct timeval *tvp;
630
668
  long int sec;
631
669
  int retval;
632
670
  VALUE read_timeout;
633
671
 
634
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
672
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
635
673
 
636
674
  tvp = NULL;
637
675
  if (!NIL_P(read_timeout)) {
@@ -652,7 +690,7 @@ static VALUE do_query(void *args) {
652
690
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
653
691
 
654
692
  if (retval == 0) {
655
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
693
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
656
694
  }
657
695
 
658
696
  if (retval < 0) {
@@ -747,7 +785,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
747
785
  * Query the database with +sql+, with optional +options+. For the possible
748
786
  * options, see default_query_options on the Mysql2::Client class.
749
787
  */
750
- static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
788
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
751
789
  #ifndef _WIN32
752
790
  struct async_query_args async_args;
753
791
  #endif
@@ -759,15 +797,11 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
759
797
 
760
798
  (void)RB_GC_GUARD(current);
761
799
  Check_Type(current, T_HASH);
762
- rb_iv_set(self, "@current_query_options", current);
800
+ rb_ivar_set(self, intern_current_query_options, current);
763
801
 
764
802
  Check_Type(sql, T_STRING);
765
- #ifdef HAVE_RUBY_ENCODING_H
766
803
  /* ensure the string is in the encoding the connection is expecting */
767
804
  args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
768
- #else
769
- args.sql = sql;
770
- #endif
771
805
  args.sql_ptr = RSTRING_PTR(args.sql);
772
806
  args.sql_len = RSTRING_LEN(args.sql);
773
807
  args.wrapper = wrapper;
@@ -776,6 +810,7 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
776
810
 
777
811
  #ifndef _WIN32
778
812
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
813
+ (void)RB_GC_GUARD(sql);
779
814
 
780
815
  if (rb_hash_aref(current, sym_async) == Qtrue) {
781
816
  return Qnil;
@@ -788,7 +823,8 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
788
823
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
789
824
  }
790
825
  #else
791
- do_send_query(&args);
826
+ do_send_query((VALUE)&args);
827
+ (void)RB_GC_GUARD(sql);
792
828
 
793
829
  /* this will just block until the result is ready */
794
830
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
@@ -804,20 +840,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
804
840
  unsigned char *newStr;
805
841
  VALUE rb_str;
806
842
  unsigned long newLen, oldLen;
807
- #ifdef HAVE_RUBY_ENCODING_H
808
843
  rb_encoding *default_internal_enc;
809
844
  rb_encoding *conn_enc;
810
- #endif
811
845
  GET_CLIENT(self);
812
846
 
813
847
  REQUIRE_CONNECTED(wrapper);
814
848
  Check_Type(str, T_STRING);
815
- #ifdef HAVE_RUBY_ENCODING_H
816
849
  default_internal_enc = rb_default_internal_encoding();
817
850
  conn_enc = rb_to_encoding(wrapper->encoding);
818
851
  /* ensure the string is in the encoding the connection is expecting */
819
852
  str = rb_str_export_to_enc(str, conn_enc);
820
- #endif
821
853
 
822
854
  oldLen = RSTRING_LEN(str);
823
855
  newStr = xmalloc(oldLen*2+1);
@@ -825,21 +857,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
825
857
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
826
858
  if (newLen == oldLen) {
827
859
  /* no need to return a new ruby string if nothing changed */
828
- #ifdef HAVE_RUBY_ENCODING_H
829
860
  if (default_internal_enc) {
830
861
  str = rb_str_export_to_enc(str, default_internal_enc);
831
862
  }
832
- #endif
833
863
  xfree(newStr);
834
864
  return str;
835
865
  } else {
836
866
  rb_str = rb_str_new((const char*)newStr, newLen);
837
- #ifdef HAVE_RUBY_ENCODING_H
838
867
  rb_enc_associate(rb_str, conn_enc);
839
868
  if (default_internal_enc) {
840
869
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
841
870
  }
842
- #endif
843
871
  xfree(newStr);
844
872
  return rb_str;
845
873
  }
@@ -850,7 +878,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
850
878
  const void *retval = NULL;
851
879
  unsigned int intval = 0;
852
880
  const char * charval = NULL;
853
- bool boolval;
881
+ my_bool boolval;
854
882
 
855
883
  GET_CLIENT(self);
856
884
 
@@ -907,6 +935,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
907
935
  retval = charval;
908
936
  break;
909
937
 
938
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
939
+ case MYSQL_DEFAULT_AUTH:
940
+ charval = (const char *)StringValueCStr(value);
941
+ retval = charval;
942
+ break;
943
+ #endif
944
+
910
945
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
911
946
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
912
947
  boolval = (value == Qfalse ? 0 : 1);
@@ -950,10 +985,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
950
985
  version = rb_str_new2(mysql_get_client_info());
951
986
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
952
987
 
953
- #ifdef HAVE_RUBY_ENCODING_H
954
988
  rb_enc_associate(version, rb_usascii_encoding());
955
989
  rb_enc_associate(header_version, rb_usascii_encoding());
956
- #endif
957
990
 
958
991
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
959
992
  rb_hash_aset(version_info, sym_version, version);
@@ -969,27 +1002,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
969
1002
  */
970
1003
  static VALUE rb_mysql_client_server_info(VALUE self) {
971
1004
  VALUE version, server_info;
972
- #ifdef HAVE_RUBY_ENCODING_H
973
1005
  rb_encoding *default_internal_enc;
974
1006
  rb_encoding *conn_enc;
975
- #endif
976
1007
  GET_CLIENT(self);
977
1008
 
978
1009
  REQUIRE_CONNECTED(wrapper);
979
- #ifdef HAVE_RUBY_ENCODING_H
980
1010
  default_internal_enc = rb_default_internal_encoding();
981
1011
  conn_enc = rb_to_encoding(wrapper->encoding);
982
- #endif
983
1012
 
984
1013
  version = rb_hash_new();
985
1014
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
986
1015
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
987
- #ifdef HAVE_RUBY_ENCODING_H
988
1016
  rb_enc_associate(server_info, conn_enc);
989
1017
  if (default_internal_enc) {
990
1018
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
991
1019
  }
992
- #endif
993
1020
  rb_hash_aset(version, sym_version, server_info);
994
1021
  return version;
995
1022
  }
@@ -1023,6 +1050,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
1023
1050
  return ULL2NUM(mysql_insert_id(wrapper->client));
1024
1051
  }
1025
1052
 
1053
+ /* call-seq:
1054
+ * client.session_track
1055
+ *
1056
+ * Returns information about changes to the session state on the server.
1057
+ */
1058
+ static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
1059
+ #ifdef CLIENT_SESSION_TRACK
1060
+ const char *data;
1061
+ size_t length;
1062
+ my_ulonglong retVal;
1063
+ GET_CLIENT(self);
1064
+
1065
+ REQUIRE_CONNECTED(wrapper);
1066
+ retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
1067
+ if (retVal != 0) {
1068
+ return Qnil;
1069
+ }
1070
+ VALUE rbAry = rb_ary_new();
1071
+ VALUE rbFirst = rb_str_new(data, length);
1072
+ rb_ary_push(rbAry, rbFirst);
1073
+ while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
1074
+ VALUE rbNext = rb_str_new(data, length);
1075
+ rb_ary_push(rbAry, rbNext);
1076
+ }
1077
+ return rbAry;
1078
+ #else
1079
+ return Qnil;
1080
+ #endif
1081
+ }
1082
+
1026
1083
  /* call-seq:
1027
1084
  * client.affected_rows
1028
1085
  *
@@ -1110,6 +1167,23 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1110
1167
  }
1111
1168
  }
1112
1169
 
1170
+ /* call-seq:
1171
+ * client.set_server_option(value)
1172
+ *
1173
+ * Enables or disables an option for the connection.
1174
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1175
+ * for more information.
1176
+ */
1177
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1178
+ GET_CLIENT(self);
1179
+
1180
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1181
+ return Qtrue;
1182
+ } else {
1183
+ return Qfalse;
1184
+ }
1185
+ }
1186
+
1113
1187
  /* call-seq:
1114
1188
  * client.more_results?
1115
1189
  *
@@ -1168,7 +1242,8 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1168
1242
  return Qnil;
1169
1243
  }
1170
1244
 
1171
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1245
+ // Duplicate the options hash and put the copy in the Result object
1246
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1172
1247
  (void)RB_GC_GUARD(current);
1173
1248
  Check_Type(current, T_HASH);
1174
1249
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -1176,7 +1251,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1176
1251
  return resultObj;
1177
1252
  }
1178
1253
 
1179
- #ifdef HAVE_RUBY_ENCODING_H
1180
1254
  /* call-seq:
1181
1255
  * client.encoding
1182
1256
  *
@@ -1186,7 +1260,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1186
1260
  GET_CLIENT(self);
1187
1261
  return wrapper->encoding;
1188
1262
  }
1189
- #endif
1190
1263
 
1191
1264
  /* call-seq:
1192
1265
  * client.automatic_close?
@@ -1256,7 +1329,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1256
1329
  /* Set the instance variable here even though _mysql_client_options
1257
1330
  might not succeed, because the timeout is used in other ways
1258
1331
  elsewhere */
1259
- rb_iv_set(self, "@read_timeout", value);
1332
+ rb_ivar_set(self, intern_read_timeout, value);
1260
1333
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1261
1334
  }
1262
1335
 
@@ -1272,17 +1345,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1272
1345
 
1273
1346
  static VALUE set_charset_name(VALUE self, VALUE value) {
1274
1347
  char *charset_name;
1275
- #ifdef HAVE_RUBY_ENCODING_H
1276
1348
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1277
1349
  rb_encoding *enc;
1278
1350
  VALUE rb_enc;
1279
- #endif
1280
1351
  GET_CLIENT(self);
1281
1352
 
1282
1353
  Check_Type(value, T_STRING);
1283
1354
  charset_name = RSTRING_PTR(value);
1284
1355
 
1285
- #ifdef HAVE_RUBY_ENCODING_H
1286
1356
  mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1287
1357
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1288
1358
  VALUE inspect = rb_inspect(value);
@@ -1292,7 +1362,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1292
1362
  rb_enc = rb_enc_from_encoding(enc);
1293
1363
  wrapper->encoding = rb_enc;
1294
1364
  }
1295
- #endif
1296
1365
 
1297
1366
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1298
1367
  /* TODO: warning - unable to set charset */
@@ -1336,6 +1405,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1336
1405
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1337
1406
  }
1338
1407
 
1408
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1409
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1410
+ return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1411
+ #else
1412
+ rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
1413
+ #endif
1414
+ }
1415
+
1339
1416
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
1417
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
1418
  return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
@@ -1397,6 +1474,7 @@ void init_mysql2_client() {
1397
1474
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1398
1475
  #endif
1399
1476
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1477
+ rb_global_variable(&cMysql2Client);
1400
1478
 
1401
1479
  rb_define_alloc_func(cMysql2Client, allocate);
1402
1480
 
@@ -1416,6 +1494,7 @@ void init_mysql2_client() {
1416
1494
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1417
1495
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1418
1496
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1497
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1419
1498
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1420
1499
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1421
1500
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
@@ -1425,9 +1504,8 @@ void init_mysql2_client() {
1425
1504
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1426
1505
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1427
1506
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1428
- #ifdef HAVE_RUBY_ENCODING_H
1429
1507
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1430
- #endif
1508
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1431
1509
 
1432
1510
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1433
1511
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1438,12 +1516,13 @@ void init_mysql2_client() {
1438
1516
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1439
1517
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1440
1518
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1519
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1441
1520
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1442
1521
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1443
1522
  rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1444
1523
  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);
1524
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1525
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1447
1526
 
1448
1527
  sym_id = ID2SYM(rb_intern("id"));
1449
1528
  sym_version = ID2SYM(rb_intern("version"));
@@ -1454,10 +1533,16 @@ void init_mysql2_client() {
1454
1533
  sym_array = ID2SYM(rb_intern("array"));
1455
1534
  sym_stream = ID2SYM(rb_intern("stream"));
1456
1535
 
1536
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1537
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1538
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1539
+
1457
1540
  intern_brackets = rb_intern("[]");
1458
1541
  intern_merge = rb_intern("merge");
1459
1542
  intern_merge_bang = rb_intern("merge!");
1460
1543
  intern_new_with_args = rb_intern("new_with_args");
1544
+ intern_current_query_options = rb_intern("@current_query_options");
1545
+ intern_read_timeout = rb_intern("@read_timeout");
1461
1546
 
1462
1547
  #ifdef CLIENT_LONG_PASSWORD
1463
1548
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
@@ -1543,6 +1628,16 @@ void init_mysql2_client() {
1543
1628
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1544
1629
  #endif
1545
1630
 
1631
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1632
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1633
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1634
+ #endif
1635
+
1636
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1637
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1638
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1639
+ #endif
1640
+
1546
1641
  #ifdef CLIENT_MULTI_STATEMENTS
1547
1642
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1548
1643
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1573,16 +1668,42 @@ void init_mysql2_client() {
1573
1668
  LONG2NUM(CLIENT_BASIC_FLAGS));
1574
1669
  #endif
1575
1670
 
1671
+ #ifdef CLIENT_CONNECT_ATTRS
1672
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1673
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1674
+ #else
1675
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1676
+ * but we're using it in our default connection flags. */
1677
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1678
+ INT2NUM(0));
1679
+ #endif
1680
+
1681
+ #ifdef CLIENT_SESSION_TRACK
1682
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1683
+ /* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1684
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1685
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1686
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1687
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1688
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1689
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1690
+ #endif
1691
+
1576
1692
  #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1577
1693
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1578
1694
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1579
1695
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1580
1696
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1581
1697
  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
1698
+ #else
1699
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1700
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1701
+ #endif
1702
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1583
1703
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1584
1704
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1585
1705
  #endif
1706
+ #endif
1586
1707
 
1587
1708
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1588
1709
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
@@ -1600,3 +1721,29 @@ void init_mysql2_client() {
1600
1721
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1601
1722
  #endif
1602
1723
  }
1724
+
1725
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1726
+
1727
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1728
+ VALUE server_flags = rb_hash_new();
1729
+
1730
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1731
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1732
+ #else
1733
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1734
+ #endif
1735
+
1736
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1737
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1738
+ #else
1739
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1740
+ #endif
1741
+
1742
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1743
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1744
+ #else
1745
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1746
+ #endif
1747
+
1748
+ rb_iv_set(result, "@server_flags", server_flags);
1749
+ }