mysql2 0.4.1 → 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ea76ea0b1ef19163cecb0a38f0a4bcf44b0b32c5
4
- data.tar.gz: 472478d5dd515629891b26aca5169d038df0df0e
3
+ metadata.gz: 0f6e6398c4050512cbe22c6994153fb0ab4b3d89
4
+ data.tar.gz: 8e6345e8df7b550c7248f86cb374422866ffdd38
5
5
  SHA512:
6
- metadata.gz: de4abae3ac4f88dcedf81ab2b4d24b38b22e2006b38476b986a4fe072512d7eb7a7c1c68f569f0ee30b45ffa0e3a727ea626b392e837fcde2444a099a1cb00c5
7
- data.tar.gz: 15b876980c214865b1098d4f821647f0c1fd93ab397b020214325984d1e10e36d69297739958773ae158af9ecf2aa932c408b926b18e31a1446a3d8bdfed4de1
6
+ metadata.gz: 5b820a05878ea935e6a25b4b1e34bfebaf47fc48a8ef24ff8f8920cc4ecaabfc2f0ba7cd590c8d4c51632d5e564fba11ae35880e0bf3230425a7264341e2625a
7
+ data.tar.gz: 32f8d9a642c643225386883f97305be79868984a55a541358983ab70ec07f421dc999e8a656f51044c089a2aecb697d2b217453cf2ce0ca9fe4a17f313131e8e
data/README.md CHANGED
@@ -58,6 +58,20 @@ This may be needed if you deploy to a system where these libraries
58
58
  are located somewhere different than on your build system.
59
59
  This overrides any rpath calculated by default or by the options above.
60
60
 
61
+ * `--with-sanitize[=address,cfi,integer,memory,thread,undefined]` -
62
+ Enable sanitizers for Clang / GCC. If no argument is given, try to enable
63
+ all sanitizers or fail if none are available. If a command-separated list of
64
+ specific sanitizers is given, configure will fail unless they all are available.
65
+ Note that the some sanitizers may incur a performance penalty, and the Address
66
+ Sanitizer may require a runtime library.
67
+ To see line numbers in backtraces, declare these environment variables
68
+ (adjust the llvm-symbolizer path as needed for your system):
69
+
70
+ ``` sh
71
+ export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
72
+ export ASAN_OPTIONS=symbolize=1
73
+ ```
74
+
61
75
  ### Linux and other Unixes
62
76
 
63
77
  You may need to install a package such as `libmysqlclient-dev` or `mysql-devel`;
@@ -71,6 +85,9 @@ You may use MacPorts, Homebrew, or a native MySQL installer package. The most
71
85
  common paths will be automatically searched. If you want to select a specific
72
86
  MySQL directory, use the `--with-mysql-dir` or `--with-mysql-config` options above.
73
87
 
88
+ If you have not done so already, you will need to install the XCode select tools by running
89
+ `xcode-select --install`.
90
+
74
91
  ### Windows
75
92
  Make sure that you have Ruby and the DevKit compilers installed. We recommend
76
93
  the [Ruby Installer](http://rubyinstaller.org) distribution.
@@ -150,8 +167,8 @@ by the query like this:
150
167
  ``` ruby
151
168
  headers = results.fields # <= that's an array of field names, in order
152
169
  results.each(:as => :array) do |row|
153
- # Each row is an array, ordered the same as the query results
154
- # An otter's den is called a "holt" or "couch"
170
+ # Each row is an array, ordered the same as the query results
171
+ # An otter's den is called a "holt" or "couch"
155
172
  end
156
173
  ```
157
174
 
@@ -189,6 +206,7 @@ Mysql2::Client.new(
189
206
  :reconnect = true/false,
190
207
  :local_infile = true/false,
191
208
  :secure_auth = true/false,
209
+ :ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
192
210
  :default_file = '/path/to/my.cfg',
193
211
  :default_group = 'my.cfg section',
194
212
  :init_command => sql
@@ -258,15 +276,26 @@ Yields:
258
276
  next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
259
277
  ```
260
278
 
261
- See https://gist.github.com/1367987 for using MULTI_STATEMENTS with Active Record.
262
-
263
279
  ### Secure auth
264
280
 
265
281
  Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
266
282
  When secure_auth is enabled, the server will refuse a connection if the account password is stored in old pre-MySQL 4.1 format.
267
283
  The MySQL 5.6.5 client library may also refuse to attempt a connection if provided an older format password.
268
- To bypass this restriction in the client, pass the option :secure_auth => false to Mysql2::Client.new().
269
- If using ActiveRecord, your database.yml might look something like this:
284
+ To bypass this restriction in the client, pass the option `:secure_auth => false` to Mysql2::Client.new().
285
+
286
+ ### Flags option parsing
287
+
288
+ The `:flags` parameter accepts an integer, a string, or an array. The integer
289
+ form allows the client to assemble flags from constants defined under
290
+ `Mysql2::Client` such as `Mysql2::Client::FOUND_ROWS`. Use a bitwise `|` (OR)
291
+ to specify several flags.
292
+
293
+ The string form will be split on whitespace and parsed as with the array form:
294
+ Plain flags are added to the default flags, while flags prefixed with `-`
295
+ (minus) are removed from the default flags.
296
+
297
+ This allows easier use with ActiveRecord's database.yml, avoiding the need for magic flag numbers.
298
+ For example, to disable protocol compression, and enable multiple statements and result sets:
270
299
 
271
300
  ``` yaml
272
301
  development:
@@ -277,13 +306,17 @@ development:
277
306
  password: my_password
278
307
  host: 127.0.0.1
279
308
  port: 3306
309
+ flags:
310
+ - -COMPRESS
311
+ - FOUND_ROWS
312
+ - MULTI_STATEMENTS
280
313
  secure_auth: false
281
314
  ```
282
315
 
283
316
  ### Reading a MySQL config file
284
317
 
285
318
  You may read configuration options from a MySQL configuration file by passing
286
- the `:default_file` and `:default_group` paramters. For example:
319
+ the `:default_file` and `:default_group` parameters. For example:
287
320
 
288
321
  ``` ruby
289
322
  Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
@@ -291,7 +324,7 @@ Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
291
324
 
292
325
  ### Initial command on connect and reconnect
293
326
 
294
- If you specify the init_command option, the SQL string you provide will be executed after the connection is established.
327
+ If you specify the `:init_command` option, the SQL string you provide will be executed after the connection is established.
295
328
  If `:reconnect` is set to `true`, init_command will also be executed after a successful reconnect.
296
329
  It is useful if you want to provide session options which survive reconnection.
297
330
 
@@ -369,6 +402,15 @@ client = Mysql2::Client.new
369
402
  result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
370
403
  ```
371
404
 
405
+ Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
406
+
407
+ ``` ruby
408
+ client = Mysql2::Client.new
409
+ result = client.query("SELECT true", :cast_booleans => true)
410
+ ```
411
+
412
+ CAST function wouldn't help here as there's no way to cast to TINYINT(1). Apparently the only way to solve this is to use a stored procedure with return type set to TINYINT(1).
413
+
372
414
  ### Skipping casting
373
415
 
374
416
  Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false. (Note that :cast => false overrides :cast_booleans => true.)
@@ -455,20 +497,21 @@ As for field values themselves, I'm workin on it - but expect that soon.
455
497
 
456
498
  This gem is tested with the following Ruby versions on Linux and Mac OS X:
457
499
 
458
- * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x
500
+ * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
459
501
  * Ruby Enterprise Edition (based on MRI 1.8.7)
460
- * Rubinius 2.x
502
+ * Rubinius 2.x and 3.x do work but may fail under some workloads
461
503
 
462
504
  This gem is tested with the following MySQL and MariaDB versions:
463
505
 
464
- * MySQL 5.5, 5.7
506
+ * MySQL 5.5, 5.6, 5.7, 8.0
465
507
  * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
466
- * MariaDB 5.5, 10.0
508
+ * MariaDB 5.5, 10.0, 10.1
467
509
 
468
- ### Active Record
510
+ ### Ruby on Rails / Active Record
469
511
 
470
- * mysql2 0.2.x includes an Active Record driver compatible with AR 2.3 and 3.0
471
- * mysql2 0.3.x does not include an AR driver because it is included in AR 3.1 and above
512
+ * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
513
+ * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
514
+ * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
472
515
 
473
516
  ### Asynchronous Active Record
474
517
 
data/ext/mysql2/client.c CHANGED
@@ -30,6 +30,12 @@ VALUE rb_hash_dup(VALUE other) {
30
30
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
31
  }
32
32
 
33
+ #define REQUIRE_CONNECTED(wrapper) \
34
+ REQUIRE_INITIALIZED(wrapper) \
35
+ if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
37
+ }
38
+
33
39
  #define REQUIRE_NOT_CONNECTED(wrapper) \
34
40
  REQUIRE_INITIALIZED(wrapper) \
35
41
  if (wrapper->connected) { \
@@ -47,6 +53,16 @@ VALUE rb_hash_dup(VALUE other) {
47
53
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
48
54
  #endif
49
55
 
56
+ /*
57
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
58
+ */
59
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
60
+ #define SSL_MODE_DISABLED 1
61
+ #define SSL_MODE_REQUIRED 3
62
+ #define HAVE_CONST_SSL_MODE_DISABLED
63
+ #define HAVE_CONST_SSL_MODE_REQUIRED
64
+ #endif
65
+
50
66
  /*
51
67
  * used to pass all arguments to mysql_real_connect while inside
52
68
  * rb_thread_call_without_gvl
@@ -83,6 +99,43 @@ struct nogvl_select_db_args {
83
99
  char *db;
84
100
  };
85
101
 
102
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
103
+ unsigned long version = mysql_get_client_version();
104
+
105
+ if (version < 50703) {
106
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
107
+ return Qnil;
108
+ }
109
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
110
+ GET_CLIENT(self);
111
+ int val = NUM2INT( setting );
112
+ if (version >= 50703 && version < 50711) {
113
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
114
+ bool b = ( val == SSL_MODE_REQUIRED );
115
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
116
+ return INT2NUM(result);
117
+
118
+ } else {
119
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
120
+ return Qnil;
121
+ }
122
+ }
123
+ #endif
124
+ #ifdef FULL_SSL_MODE_SUPPORT
125
+ GET_CLIENT(self);
126
+ int val = NUM2INT( setting );
127
+
128
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
129
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
130
+ }
131
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
132
+
133
+ return INT2NUM(result);
134
+ #endif
135
+ #ifdef NO_SSL_MODE_SUPPORT
136
+ return Qnil;
137
+ #endif
138
+ }
86
139
  /*
87
140
  * non-blocking mysql_*() functions that we won't be wrapping since
88
141
  * they do not appear to hit the network nor issue any interruptible
@@ -133,7 +186,7 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
133
186
 
134
187
  static void *nogvl_init(void *ptr) {
135
188
  MYSQL *client;
136
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
189
+ mysql_client_wrapper *wrapper = ptr;
137
190
 
138
191
  /* may initialize embedded server and read /etc/services off disk */
139
192
  client = mysql_init(wrapper->client);
@@ -213,6 +266,7 @@ static void *nogvl_close(void *ptr) {
213
266
 
214
267
  if (wrapper->client) {
215
268
  mysql_close(wrapper->client);
269
+ xfree(wrapper->client);
216
270
  wrapper->client = NULL;
217
271
  wrapper->connected = 0;
218
272
  wrapper->active_thread = Qnil;
@@ -223,7 +277,7 @@ static void *nogvl_close(void *ptr) {
223
277
 
224
278
  /* this is called during GC */
225
279
  static void rb_mysql_client_free(void *ptr) {
226
- mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
280
+ mysql_client_wrapper *wrapper = ptr;
227
281
  decr_mysql2_client(wrapper);
228
282
  }
229
283
 
@@ -233,7 +287,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
233
287
 
234
288
  if (wrapper->refcount == 0) {
235
289
  #ifndef _WIN32
236
- if (wrapper->connected) {
290
+ if (wrapper->connected && !wrapper->automatic_close) {
237
291
  /* The client is being garbage collected while connected. Prevent
238
292
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
239
293
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -249,7 +303,6 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
249
303
  #endif
250
304
 
251
305
  nogvl_close(wrapper);
252
- xfree(wrapper->client);
253
306
  xfree(wrapper);
254
307
  }
255
308
  }
@@ -259,7 +312,8 @@ static VALUE allocate(VALUE klass) {
259
312
  mysql_client_wrapper * wrapper;
260
313
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
314
  wrapper->encoding = Qnil;
262
- MARK_CONN_INACTIVE(self);
315
+ wrapper->active_thread = Qnil;
316
+ wrapper->automatic_close = 1;
263
317
  wrapper->server_version = 0;
264
318
  wrapper->reconnect_enabled = 0;
265
319
  wrapper->connect_timeout = 0;
@@ -331,6 +385,26 @@ static VALUE rb_mysql_info(VALUE self) {
331
385
  return rb_str;
332
386
  }
333
387
 
388
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
389
+ {
390
+ const char *cipher;
391
+ VALUE rb_str;
392
+ GET_CLIENT(self);
393
+
394
+ cipher = mysql_get_ssl_cipher(wrapper->client);
395
+
396
+ if (cipher == NULL) {
397
+ return Qnil;
398
+ }
399
+
400
+ rb_str = rb_str_new2(cipher);
401
+ #ifdef HAVE_RUBY_ENCODING_H
402
+ rb_enc_associate(rb_str, rb_utf8_encoding());
403
+ #endif
404
+
405
+ return rb_str;
406
+ }
407
+
334
408
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
335
409
  struct nogvl_connect_args args;
336
410
  time_t start_time, end_time, elapsed_time, connect_timeout;
@@ -372,7 +446,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
446
  if (wrapper->connect_timeout)
373
447
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
448
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
449
+ rb_raise_mysql2_error(wrapper);
376
450
  }
377
451
 
378
452
  wrapper->server_version = mysql_get_server_version(wrapper->client);
@@ -381,13 +455,12 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
381
455
  }
382
456
 
383
457
  /*
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.
458
+ * Immediately disconnect from the server; normally the garbage collector
459
+ * will disconnect automatically when a connection is no longer needed.
460
+ * Explicitly closing this will free up server resources sooner than waiting
461
+ * for the garbage collector.
388
462
  *
389
- * @see http://dev.mysql.com/doc/en/communication-errors.html
390
- * @return [void]
463
+ * @return [nil]
391
464
  */
392
465
  static VALUE rb_mysql_client_close(VALUE self) {
393
466
  GET_CLIENT(self);
@@ -418,8 +491,8 @@ static VALUE do_send_query(void *args) {
418
491
  mysql_client_wrapper *wrapper = query_args->wrapper;
419
492
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
420
493
  /* an error occurred, we're not active anymore */
421
- MARK_CONN_INACTIVE(self);
422
- return rb_raise_mysql2_error(wrapper);
494
+ wrapper->active_thread = Qnil;
495
+ rb_raise_mysql2_error(wrapper);
423
496
  }
424
497
  return Qnil;
425
498
  }
@@ -431,16 +504,15 @@ static VALUE do_send_query(void *args) {
431
504
  */
432
505
  static void *nogvl_read_query_result(void *ptr) {
433
506
  MYSQL * client = ptr;
434
- my_bool res = mysql_read_query_result(client);
507
+ bool res = mysql_read_query_result(client);
435
508
 
436
509
  return (void *)(res == 0 ? Qtrue : Qfalse);
437
510
  }
438
511
 
439
512
  static void *nogvl_do_result(void *ptr, char use_result) {
440
- mysql_client_wrapper *wrapper;
513
+ mysql_client_wrapper *wrapper = ptr;
441
514
  MYSQL_RES *result;
442
515
 
443
- wrapper = (mysql_client_wrapper *)ptr;
444
516
  if (use_result) {
445
517
  result = mysql_use_result(wrapper->client);
446
518
  } else {
@@ -449,7 +521,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
449
521
 
450
522
  /* once our result is stored off, this connection is
451
523
  ready for another command to be issued */
452
- MARK_CONN_INACTIVE(self);
524
+ wrapper->active_thread = Qnil;
453
525
 
454
526
  return result;
455
527
  }
@@ -481,8 +553,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
481
553
  REQUIRE_CONNECTED(wrapper);
482
554
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
483
555
  /* an error occurred, mark this connection inactive */
484
- MARK_CONN_INACTIVE(self);
485
- return rb_raise_mysql2_error(wrapper);
556
+ wrapper->active_thread = Qnil;
557
+ rb_raise_mysql2_error(wrapper);
486
558
  }
487
559
 
488
560
  is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
@@ -494,7 +566,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
494
566
 
495
567
  if (result == NULL) {
496
568
  if (mysql_errno(wrapper->client) != 0) {
497
- MARK_CONN_INACTIVE(self);
569
+ wrapper->active_thread = Qnil;
498
570
  rb_raise_mysql2_error(wrapper);
499
571
  }
500
572
  /* no data and no error, so query was not a SELECT */
@@ -518,29 +590,30 @@ struct async_query_args {
518
590
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
519
591
  GET_CLIENT(self);
520
592
 
521
- MARK_CONN_INACTIVE(self);
593
+ wrapper->active_thread = Qnil;
522
594
  wrapper->connected = 0;
523
595
 
524
596
  /* Invalidate the MySQL socket to prevent further communication.
525
597
  * The GC will come along later and call mysql_close to free it.
526
598
  */
527
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
528
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
529
- close(wrapper->client->net.fd);
599
+ if (wrapper->client) {
600
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
601
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
602
+ close(wrapper->client->net.fd);
603
+ }
530
604
  }
531
605
 
532
606
  rb_exc_raise(error);
533
607
  }
534
608
 
535
609
  static VALUE do_query(void *args) {
536
- struct async_query_args *async_args;
610
+ struct async_query_args *async_args = args;
537
611
  struct timeval tv;
538
- struct timeval* tvp;
612
+ struct timeval *tvp;
539
613
  long int sec;
540
614
  int retval;
541
615
  VALUE read_timeout;
542
616
 
543
- async_args = (struct async_query_args *)args;
544
617
  read_timeout = rb_iv_get(async_args->self, "@read_timeout");
545
618
 
546
619
  tvp = NULL;
@@ -576,28 +649,30 @@ static VALUE do_query(void *args) {
576
649
 
577
650
  return Qnil;
578
651
  }
579
- #else
580
- static VALUE finish_and_mark_inactive(void *args) {
581
- VALUE self;
582
- MYSQL_RES *result;
583
-
584
- self = (VALUE)args;
652
+ #endif
585
653
 
654
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
586
655
  GET_CLIENT(self);
587
656
 
657
+ /* Check if execution terminated while result was still being read. */
588
658
  if (!NIL_P(wrapper->active_thread)) {
589
- /* if we got here, the result hasn't been read off the wire yet
590
- so lets do that and then throw it away because we have no way
591
- of getting it back up to the caller from here */
592
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
593
- mysql_free_result(result);
594
-
595
- MARK_CONN_INACTIVE(self);
659
+ /* Invalidate the MySQL socket to prevent further communication. */
660
+ #ifndef _WIN32
661
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
662
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
663
+ close(wrapper->client->net.fd);
664
+ }
665
+ #else
666
+ close(wrapper->client->net.fd);
667
+ #endif
668
+ /* Skip mysql client check performed before command execution. */
669
+ wrapper->client->status = MYSQL_STATUS_READY;
670
+ wrapper->active_thread = Qnil;
671
+ wrapper->connected = 0;
596
672
  }
597
673
 
598
674
  return Qnil;
599
675
  }
600
- #endif
601
676
 
602
677
  void rb_mysql_client_set_active_thread(VALUE self) {
603
678
  VALUE thread_current = rb_thread_current();
@@ -691,13 +766,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
691
766
 
692
767
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
693
768
 
694
- return rb_mysql_client_async_result(self);
769
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
695
770
  }
696
771
  #else
697
772
  do_send_query(&args);
698
773
 
699
774
  /* this will just block until the result is ready */
700
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
775
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
701
776
  #endif
702
777
  }
703
778
 
@@ -756,7 +831,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
756
831
  const void *retval = NULL;
757
832
  unsigned int intval = 0;
758
833
  const char * charval = NULL;
759
- my_bool boolval;
834
+ bool boolval;
760
835
 
761
836
  GET_CLIENT(self);
762
837
 
@@ -1015,10 +1090,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1015
1090
  static VALUE rb_mysql_client_more_results(VALUE self)
1016
1091
  {
1017
1092
  GET_CLIENT(self);
1018
- if (mysql_more_results(wrapper->client) == 0)
1019
- return Qfalse;
1020
- else
1021
- return Qtrue;
1093
+ if (mysql_more_results(wrapper->client) == 0)
1094
+ return Qfalse;
1095
+ else
1096
+ return Qtrue;
1022
1097
  }
1023
1098
 
1024
1099
  /* call-seq:
@@ -1085,6 +1160,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1085
1160
  }
1086
1161
  #endif
1087
1162
 
1163
+ /* call-seq:
1164
+ * client.automatic_close?
1165
+ *
1166
+ * @return [Boolean]
1167
+ */
1168
+ static VALUE get_automatic_close(VALUE self) {
1169
+ GET_CLIENT(self);
1170
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1171
+ }
1172
+
1173
+ /* call-seq:
1174
+ * client.automatic_close = false
1175
+ *
1176
+ * Set this to +false+ to leave the connection open after it is garbage
1177
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1178
+ * call +close+ when the connection is no longer needed.
1179
+ *
1180
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1181
+ */
1182
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1183
+ GET_CLIENT(self);
1184
+ if (RTEST(value)) {
1185
+ wrapper->automatic_close = 1;
1186
+ } else {
1187
+ #ifndef _WIN32
1188
+ wrapper->automatic_close = 0;
1189
+ #else
1190
+ rb_warn("Connections are always closed by garbage collector on Windows");
1191
+ #endif
1192
+ }
1193
+ return value;
1194
+ }
1195
+
1088
1196
  /* call-seq:
1089
1197
  * client.reconnect = true
1090
1198
  *
@@ -1143,6 +1251,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1143
1251
  #endif
1144
1252
  GET_CLIENT(self);
1145
1253
 
1254
+ Check_Type(value, T_STRING);
1146
1255
  charset_name = RSTRING_PTR(value);
1147
1256
 
1148
1257
  #ifdef HAVE_RUBY_ENCODING_H
@@ -1199,7 +1308,7 @@ static VALUE initialize_ext(VALUE self) {
1199
1308
 
1200
1309
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1201
1310
  /* TODO: warning - not enough memory? */
1202
- return rb_raise_mysql2_error(wrapper);
1311
+ rb_raise_mysql2_error(wrapper);
1203
1312
  }
1204
1313
 
1205
1314
  wrapper->initialized = 1;
@@ -1268,9 +1377,12 @@ void init_mysql2_client() {
1268
1377
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1269
1378
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1270
1379
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1380
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1381
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1271
1382
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1272
1383
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1273
1384
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1385
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1274
1386
  #ifdef HAVE_RUBY_ENCODING_H
1275
1387
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1276
1388
  #endif
@@ -1285,6 +1397,7 @@ void init_mysql2_client() {
1285
1397
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1286
1398
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1287
1399
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1400
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1288
1401
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1289
1402
  rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1290
1403
  rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
@@ -1412,4 +1525,31 @@ void init_mysql2_client() {
1412
1525
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1413
1526
  LONG2NUM(CLIENT_BASIC_FLAGS));
1414
1527
  #endif
1528
+
1529
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1530
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1531
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1532
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1533
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1534
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1535
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1536
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1537
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1538
+ #endif
1539
+
1540
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1541
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1542
+ #endif
1543
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1544
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1545
+ #endif
1546
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1547
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1548
+ #endif
1549
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1550
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1551
+ #endif
1552
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1553
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1554
+ #endif
1415
1555
  }