mysql2 0.4.2 → 0.5.2

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) {
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
  }
@@ -259,13 +313,14 @@ static VALUE allocate(VALUE klass) {
259
313
  mysql_client_wrapper * wrapper;
260
314
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
315
  wrapper->encoding = Qnil;
262
- MARK_CONN_INACTIVE(self);
316
+ wrapper->active_thread = Qnil;
317
+ wrapper->automatic_close = 1;
263
318
  wrapper->server_version = 0;
264
319
  wrapper->reconnect_enabled = 0;
265
320
  wrapper->connect_timeout = 0;
266
- wrapper->connected = 0; /* means that a database connection is open */
267
321
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
268
322
  wrapper->refcount = 1;
323
+ wrapper->closed = 0;
269
324
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
270
325
 
271
326
  return obj;
@@ -295,9 +350,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
295
350
  return str;
296
351
  } else {
297
352
  rb_str = rb_str_new((const char*)newStr, newLen);
298
- #ifdef HAVE_RUBY_ENCODING_H
299
353
  rb_enc_copy(rb_str, str);
300
- #endif
301
354
  xfree(newStr);
302
355
  return rb_str;
303
356
  }
@@ -324,14 +377,43 @@ static VALUE rb_mysql_info(VALUE self) {
324
377
  }
325
378
 
326
379
  rb_str = rb_str_new2(info);
327
- #ifdef HAVE_RUBY_ENCODING_H
328
380
  rb_enc_associate(rb_str, rb_utf8_encoding());
329
- #endif
330
381
 
331
382
  return rb_str;
332
383
  }
333
384
 
334
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
385
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
386
+ {
387
+ const char *cipher;
388
+ VALUE rb_str;
389
+ GET_CLIENT(self);
390
+
391
+ cipher = mysql_get_ssl_cipher(wrapper->client);
392
+
393
+ if (cipher == NULL) {
394
+ return Qnil;
395
+ }
396
+
397
+ rb_str = rb_str_new2(cipher);
398
+ rb_enc_associate(rb_str, rb_utf8_encoding());
399
+
400
+ return rb_str;
401
+ }
402
+
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) {
335
417
  struct nogvl_connect_args args;
336
418
  time_t start_time, end_time, elapsed_time, connect_timeout;
337
419
  VALUE rv;
@@ -346,6 +428,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
346
428
  args.mysql = wrapper->client;
347
429
  args.client_flag = NUM2ULONG(flags);
348
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
+
349
436
  if (wrapper->connect_timeout)
350
437
  time(&start_time);
351
438
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -372,33 +459,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
459
  if (wrapper->connect_timeout)
373
460
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
461
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
462
+ rb_raise_mysql2_error(wrapper);
376
463
  }
377
464
 
378
465
  wrapper->server_version = mysql_get_server_version(wrapper->client);
379
- wrapper->connected = 1;
380
466
  return self;
381
467
  }
382
468
 
383
469
  /*
384
- * Terminate the connection; call this when the connection is no longer needed.
385
- * The garbage collector can close the connection, but doing so emits an
386
- * "Aborted connection" error on the server and increments the Aborted_clients
387
- * status variable.
470
+ * Immediately disconnect from the server; normally the garbage collector
471
+ * will disconnect automatically when a connection is no longer needed.
472
+ * Explicitly closing this will free up server resources sooner than waiting
473
+ * for the garbage collector.
388
474
  *
389
- * @see http://dev.mysql.com/doc/en/communication-errors.html
390
- * @return [void]
475
+ * @return [nil]
391
476
  */
392
477
  static VALUE rb_mysql_client_close(VALUE self) {
393
478
  GET_CLIENT(self);
394
479
 
395
- if (wrapper->connected) {
480
+ if (wrapper->client) {
396
481
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
397
482
  }
398
483
 
399
484
  return Qnil;
400
485
  }
401
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
+
402
497
  /*
403
498
  * mysql_send_query is unlikely to block since most queries are small
404
499
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -418,8 +513,8 @@ static VALUE do_send_query(void *args) {
418
513
  mysql_client_wrapper *wrapper = query_args->wrapper;
419
514
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
420
515
  /* an error occurred, we're not active anymore */
421
- MARK_CONN_INACTIVE(self);
422
- return rb_raise_mysql2_error(wrapper);
516
+ wrapper->active_thread = Qnil;
517
+ rb_raise_mysql2_error(wrapper);
423
518
  }
424
519
  return Qnil;
425
520
  }
@@ -448,7 +543,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
448
543
 
449
544
  /* once our result is stored off, this connection is
450
545
  ready for another command to be issued */
451
- MARK_CONN_INACTIVE(self);
546
+ wrapper->active_thread = Qnil;
452
547
 
453
548
  return result;
454
549
  }
@@ -480,8 +575,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
480
575
  REQUIRE_CONNECTED(wrapper);
481
576
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
482
577
  /* an error occurred, mark this connection inactive */
483
- MARK_CONN_INACTIVE(self);
484
- return rb_raise_mysql2_error(wrapper);
578
+ wrapper->active_thread = Qnil;
579
+ rb_raise_mysql2_error(wrapper);
485
580
  }
486
581
 
487
582
  is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
@@ -493,18 +588,21 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
493
588
 
494
589
  if (result == NULL) {
495
590
  if (mysql_errno(wrapper->client) != 0) {
496
- MARK_CONN_INACTIVE(self);
591
+ wrapper->active_thread = Qnil;
497
592
  rb_raise_mysql2_error(wrapper);
498
593
  }
499
594
  /* no data and no error, so query was not a SELECT */
500
595
  return Qnil;
501
596
  }
502
597
 
598
+ // Duplicate the options hash and put the copy in the Result object
503
599
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
504
600
  (void)RB_GC_GUARD(current);
505
601
  Check_Type(current, T_HASH);
506
602
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
507
603
 
604
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
605
+
508
606
  return resultObj;
509
607
  }
510
608
 
@@ -517,15 +615,17 @@ struct async_query_args {
517
615
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
518
616
  GET_CLIENT(self);
519
617
 
520
- MARK_CONN_INACTIVE(self);
521
- wrapper->connected = 0;
618
+ wrapper->active_thread = Qnil;
522
619
 
523
620
  /* Invalidate the MySQL socket to prevent further communication.
524
621
  * The GC will come along later and call mysql_close to free it.
525
622
  */
526
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
527
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
528
- 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;
529
629
  }
530
630
 
531
631
  rb_exc_raise(error);
@@ -560,7 +660,7 @@ static VALUE do_query(void *args) {
560
660
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
561
661
 
562
662
  if (retval == 0) {
563
- 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));
564
664
  }
565
665
 
566
666
  if (retval < 0) {
@@ -574,26 +674,32 @@ static VALUE do_query(void *args) {
574
674
 
575
675
  return Qnil;
576
676
  }
577
- #else
578
- static VALUE finish_and_mark_inactive(void *args) {
579
- VALUE self = args;
580
- MYSQL_RES *result;
677
+ #endif
581
678
 
679
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
582
680
  GET_CLIENT(self);
583
681
 
682
+ /* Check if execution terminated while result was still being read. */
584
683
  if (!NIL_P(wrapper->active_thread)) {
585
- /* if we got here, the result hasn't been read off the wire yet
586
- so lets do that and then throw it away because we have no way
587
- of getting it back up to the caller from here */
588
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
589
- mysql_free_result(result);
590
-
591
- MARK_CONN_INACTIVE(self);
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;
698
+ wrapper->active_thread = Qnil;
592
699
  }
593
700
 
594
701
  return Qnil;
595
702
  }
596
- #endif
597
703
 
598
704
  void rb_mysql_client_set_active_thread(VALUE self) {
599
705
  VALUE thread_current = rb_thread_current();
@@ -649,7 +755,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
649
755
  * Query the database with +sql+, with optional +options+. For the possible
650
756
  * options, see default_query_options on the Mysql2::Client class.
651
757
  */
652
- static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
758
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
653
759
  #ifndef _WIN32
654
760
  struct async_query_args async_args;
655
761
  #endif
@@ -664,12 +770,8 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
664
770
  rb_iv_set(self, "@current_query_options", current);
665
771
 
666
772
  Check_Type(sql, T_STRING);
667
- #ifdef HAVE_RUBY_ENCODING_H
668
773
  /* ensure the string is in the encoding the connection is expecting */
669
774
  args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
670
- #else
671
- args.sql = sql;
672
- #endif
673
775
  args.sql_ptr = RSTRING_PTR(args.sql);
674
776
  args.sql_len = RSTRING_LEN(args.sql);
675
777
  args.wrapper = wrapper;
@@ -687,13 +789,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
687
789
 
688
790
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
689
791
 
690
- return rb_mysql_client_async_result(self);
792
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
691
793
  }
692
794
  #else
693
795
  do_send_query(&args);
694
796
 
695
797
  /* this will just block until the result is ready */
696
- 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);
697
799
  #endif
698
800
  }
699
801
 
@@ -706,20 +808,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
706
808
  unsigned char *newStr;
707
809
  VALUE rb_str;
708
810
  unsigned long newLen, oldLen;
709
- #ifdef HAVE_RUBY_ENCODING_H
710
811
  rb_encoding *default_internal_enc;
711
812
  rb_encoding *conn_enc;
712
- #endif
713
813
  GET_CLIENT(self);
714
814
 
715
815
  REQUIRE_CONNECTED(wrapper);
716
816
  Check_Type(str, T_STRING);
717
- #ifdef HAVE_RUBY_ENCODING_H
718
817
  default_internal_enc = rb_default_internal_encoding();
719
818
  conn_enc = rb_to_encoding(wrapper->encoding);
720
819
  /* ensure the string is in the encoding the connection is expecting */
721
820
  str = rb_str_export_to_enc(str, conn_enc);
722
- #endif
723
821
 
724
822
  oldLen = RSTRING_LEN(str);
725
823
  newStr = xmalloc(oldLen*2+1);
@@ -727,21 +825,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
727
825
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
728
826
  if (newLen == oldLen) {
729
827
  /* no need to return a new ruby string if nothing changed */
730
- #ifdef HAVE_RUBY_ENCODING_H
731
828
  if (default_internal_enc) {
732
829
  str = rb_str_export_to_enc(str, default_internal_enc);
733
830
  }
734
- #endif
735
831
  xfree(newStr);
736
832
  return str;
737
833
  } else {
738
834
  rb_str = rb_str_new((const char*)newStr, newLen);
739
- #ifdef HAVE_RUBY_ENCODING_H
740
835
  rb_enc_associate(rb_str, conn_enc);
741
836
  if (default_internal_enc) {
742
837
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
743
838
  }
744
- #endif
745
839
  xfree(newStr);
746
840
  return rb_str;
747
841
  }
@@ -787,10 +881,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
787
881
  retval = &boolval;
788
882
  break;
789
883
 
884
+ #ifdef MYSQL_SECURE_AUTH
790
885
  case MYSQL_SECURE_AUTH:
791
886
  boolval = (value == Qfalse ? 0 : 1);
792
887
  retval = &boolval;
793
888
  break;
889
+ #endif
794
890
 
795
891
  case MYSQL_READ_DEFAULT_FILE:
796
892
  charval = (const char *)StringValueCStr(value);
@@ -807,6 +903,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
807
903
  retval = charval;
808
904
  break;
809
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
+
810
913
  default:
811
914
  return Qfalse;
812
915
  }
@@ -843,10 +946,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
843
946
  version = rb_str_new2(mysql_get_client_info());
844
947
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
845
948
 
846
- #ifdef HAVE_RUBY_ENCODING_H
847
949
  rb_enc_associate(version, rb_usascii_encoding());
848
950
  rb_enc_associate(header_version, rb_usascii_encoding());
849
- #endif
850
951
 
851
952
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
852
953
  rb_hash_aset(version_info, sym_version, version);
@@ -862,27 +963,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
862
963
  */
863
964
  static VALUE rb_mysql_client_server_info(VALUE self) {
864
965
  VALUE version, server_info;
865
- #ifdef HAVE_RUBY_ENCODING_H
866
966
  rb_encoding *default_internal_enc;
867
967
  rb_encoding *conn_enc;
868
- #endif
869
968
  GET_CLIENT(self);
870
969
 
871
970
  REQUIRE_CONNECTED(wrapper);
872
- #ifdef HAVE_RUBY_ENCODING_H
873
971
  default_internal_enc = rb_default_internal_encoding();
874
972
  conn_enc = rb_to_encoding(wrapper->encoding);
875
- #endif
876
973
 
877
974
  version = rb_hash_new();
878
975
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
879
976
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
880
- #ifdef HAVE_RUBY_ENCODING_H
881
977
  rb_enc_associate(server_info, conn_enc);
882
978
  if (default_internal_enc) {
883
979
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
884
980
  }
885
- #endif
886
981
  rb_hash_aset(version, sym_version, server_info);
887
982
  return version;
888
983
  }
@@ -996,13 +1091,30 @@ static void *nogvl_ping(void *ptr) {
996
1091
  static VALUE rb_mysql_client_ping(VALUE self) {
997
1092
  GET_CLIENT(self);
998
1093
 
999
- if (!wrapper->connected) {
1094
+ if (!CONNECTED(wrapper)) {
1000
1095
  return Qfalse;
1001
1096
  } else {
1002
1097
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
1003
1098
  }
1004
1099
  }
1005
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
+
1006
1118
  /* call-seq:
1007
1119
  * client.more_results?
1008
1120
  *
@@ -1011,10 +1123,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1011
1123
  static VALUE rb_mysql_client_more_results(VALUE self)
1012
1124
  {
1013
1125
  GET_CLIENT(self);
1014
- if (mysql_more_results(wrapper->client) == 0)
1015
- return Qfalse;
1016
- else
1017
- return Qtrue;
1126
+ if (mysql_more_results(wrapper->client) == 0)
1127
+ return Qfalse;
1128
+ else
1129
+ return Qtrue;
1018
1130
  }
1019
1131
 
1020
1132
  /* call-seq:
@@ -1061,6 +1173,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1061
1173
  return Qnil;
1062
1174
  }
1063
1175
 
1176
+ // Duplicate the options hash and put the copy in the Result object
1064
1177
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1065
1178
  (void)RB_GC_GUARD(current);
1066
1179
  Check_Type(current, T_HASH);
@@ -1069,7 +1182,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1069
1182
  return resultObj;
1070
1183
  }
1071
1184
 
1072
- #ifdef HAVE_RUBY_ENCODING_H
1073
1185
  /* call-seq:
1074
1186
  * client.encoding
1075
1187
  *
@@ -1079,7 +1191,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1079
1191
  GET_CLIENT(self);
1080
1192
  return wrapper->encoding;
1081
1193
  }
1194
+
1195
+ /* call-seq:
1196
+ * client.automatic_close?
1197
+ *
1198
+ * @return [Boolean]
1199
+ */
1200
+ static VALUE get_automatic_close(VALUE self) {
1201
+ GET_CLIENT(self);
1202
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1203
+ }
1204
+
1205
+ /* call-seq:
1206
+ * client.automatic_close = false
1207
+ *
1208
+ * Set this to +false+ to leave the connection open after it is garbage
1209
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1210
+ * call +close+ when the connection is no longer needed.
1211
+ *
1212
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1213
+ */
1214
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1215
+ GET_CLIENT(self);
1216
+ if (RTEST(value)) {
1217
+ wrapper->automatic_close = 1;
1218
+ } else {
1219
+ #ifndef _WIN32
1220
+ wrapper->automatic_close = 0;
1221
+ #else
1222
+ rb_warn("Connections are always closed by garbage collector on Windows");
1082
1223
  #endif
1224
+ }
1225
+ return value;
1226
+ }
1083
1227
 
1084
1228
  /* call-seq:
1085
1229
  * client.reconnect = true
@@ -1132,16 +1276,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1132
1276
 
1133
1277
  static VALUE set_charset_name(VALUE self, VALUE value) {
1134
1278
  char *charset_name;
1135
- #ifdef HAVE_RUBY_ENCODING_H
1136
1279
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1137
1280
  rb_encoding *enc;
1138
1281
  VALUE rb_enc;
1139
- #endif
1140
1282
  GET_CLIENT(self);
1141
1283
 
1284
+ Check_Type(value, T_STRING);
1142
1285
  charset_name = RSTRING_PTR(value);
1143
1286
 
1144
- #ifdef HAVE_RUBY_ENCODING_H
1145
1287
  mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1146
1288
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1147
1289
  VALUE inspect = rb_inspect(value);
@@ -1151,7 +1293,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1151
1293
  rb_enc = rb_enc_from_encoding(enc);
1152
1294
  wrapper->encoding = rb_enc;
1153
1295
  }
1154
- #endif
1155
1296
 
1156
1297
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1157
1298
  /* TODO: warning - unable to set charset */
@@ -1175,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1175
1316
  }
1176
1317
 
1177
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
1178
1321
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1322
+ #else
1323
+ return Qfalse;
1324
+ #endif
1179
1325
  }
1180
1326
 
1181
1327
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1190,12 +1336,20 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1190
1336
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1191
1337
  }
1192
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
+
1193
1347
  static VALUE initialize_ext(VALUE self) {
1194
1348
  GET_CLIENT(self);
1195
1349
 
1196
1350
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1197
1351
  /* TODO: warning - not enough memory? */
1198
- return rb_raise_mysql2_error(wrapper);
1352
+ rb_raise_mysql2_error(wrapper);
1199
1353
  }
1200
1354
 
1201
1355
  wrapper->initialized = 1;
@@ -1250,6 +1404,7 @@ void init_mysql2_client() {
1250
1404
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1251
1405
 
1252
1406
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1407
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1253
1408
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1254
1409
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1255
1410
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1261,15 +1416,17 @@ void init_mysql2_client() {
1261
1416
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1262
1417
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1263
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);
1264
1420
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1265
1421
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1266
1422
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1423
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1424
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1267
1425
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1268
1426
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1269
1427
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1270
- #ifdef HAVE_RUBY_ENCODING_H
1428
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1271
1429
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1272
- #endif
1273
1430
 
1274
1431
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1275
1432
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1281,9 +1438,11 @@ void init_mysql2_client() {
1281
1438
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1282
1439
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1283
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);
1284
1443
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1285
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1286
- 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);
1287
1446
 
1288
1447
  sym_id = ID2SYM(rb_intern("id"));
1289
1448
  sym_version = ID2SYM(rb_intern("version"));
@@ -1294,6 +1453,10 @@ void init_mysql2_client() {
1294
1453
  sym_array = ID2SYM(rb_intern("array"));
1295
1454
  sym_stream = ID2SYM(rb_intern("stream"));
1296
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
+
1297
1460
  intern_brackets = rb_intern("[]");
1298
1461
  intern_merge = rb_intern("merge");
1299
1462
  intern_merge_bang = rb_intern("merge!");
@@ -1302,6 +1465,10 @@ void init_mysql2_client() {
1302
1465
  #ifdef CLIENT_LONG_PASSWORD
1303
1466
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1304
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));
1305
1472
  #endif
1306
1473
 
1307
1474
  #ifdef CLIENT_FOUND_ROWS
@@ -1379,6 +1546,16 @@ void init_mysql2_client() {
1379
1546
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1380
1547
  #endif
1381
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
+
1382
1559
  #ifdef CLIENT_MULTI_STATEMENTS
1383
1560
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1384
1561
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1408,4 +1585,67 @@ void init_mysql2_client() {
1408
1585
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1409
1586
  LONG2NUM(CLIENT_BASIC_FLAGS));
1410
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);
1411
1651
  }