mysql2 0.4.2-x64-mingw32 → 0.4.3-x64-mingw32

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 898360bc199dd9b5272557e71f84eae8a4820048
4
- data.tar.gz: 565eb79e0857133073ce3815efd266dbef07c08d
3
+ metadata.gz: 02b06d1845bb8383384eb08fd3aed4d51e65a52f
4
+ data.tar.gz: 78a3ea0d05dd09bdfe0daa41bc2d18951c932afd
5
5
  SHA512:
6
- metadata.gz: 70bd32ac6361cb6f009e9e6b7af2209773548903b0c33d30c319ec71f6ecfd377cdec7b81b2938946f90e2477c67307a4c9e8c85daa8005f575633019a73af95
7
- data.tar.gz: 31b37b7f6711c45f7b4453118b856634b6ad255624ae2a346d21c0515daeb9f3033496a32edb77dab9fb6e29c9bd294190be3ae28443b2f1f239a7c7a2441d91
6
+ metadata.gz: 4e9ab24baf5ac85380fe6d14198b88ae2cc1294a385c85a0a99ae48fd3197f3a6ff790b41db6269b6a03593394ff61cd693fc62f225c087fb52ddb164756a709
7
+ data.tar.gz: 5be83a0f74b810a12549557919594d7e992fd5ea9279ae252470dd26b8729926b0ebfe6cf0c35cad2d3b70bb7197c9a7126f1c406f74751aafc00705a1dd1c28
data/README.md CHANGED
@@ -490,14 +490,15 @@ This gem is tested with the following Ruby versions on Linux and Mac OS X:
490
490
 
491
491
  This gem is tested with the following MySQL and MariaDB versions:
492
492
 
493
- * MySQL 5.5, 5.7
493
+ * MySQL 5.5, 5.6, 5.7
494
494
  * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
495
- * MariaDB 5.5, 10.0
495
+ * MariaDB 5.5, 10.0, 10.1
496
496
 
497
- ### Active Record
497
+ ### Rails / Active Record
498
498
 
499
- * mysql2 0.2.x includes an Active Record driver compatible with AR 2.3 and 3.0
500
- * mysql2 0.3.x does not include an AR driver because it is included in AR 3.1 and above
499
+ * mysql2 0.4.x works with Active Record 4.2.5 and higher.
500
+ * mysql2 0.3.x works with Active Record 3.1 and higher (the AR adapter is now included in AR proper).
501
+ * mysql2 0.2.x includes an Active Record adapter compatible with AR 2.3 and 3.0, and should not be used with AR 3.1 or higher.
501
502
 
502
503
  ### Asynchronous Active Record
503
504
 
@@ -234,7 +234,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
234
234
 
235
235
  if (wrapper->refcount == 0) {
236
236
  #ifndef _WIN32
237
- if (wrapper->connected) {
237
+ if (wrapper->connected && !wrapper->automatic_close) {
238
238
  /* The client is being garbage collected while connected. Prevent
239
239
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
240
240
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -259,7 +259,8 @@ static VALUE allocate(VALUE klass) {
259
259
  mysql_client_wrapper * wrapper;
260
260
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
261
261
  wrapper->encoding = Qnil;
262
- MARK_CONN_INACTIVE(self);
262
+ wrapper->active_thread = Qnil;
263
+ wrapper->automatic_close = 1;
263
264
  wrapper->server_version = 0;
264
265
  wrapper->reconnect_enabled = 0;
265
266
  wrapper->connect_timeout = 0;
@@ -331,6 +332,26 @@ static VALUE rb_mysql_info(VALUE self) {
331
332
  return rb_str;
332
333
  }
333
334
 
335
+ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
336
+ {
337
+ const char *cipher;
338
+ VALUE rb_str;
339
+ GET_CLIENT(self);
340
+
341
+ cipher = mysql_get_ssl_cipher(wrapper->client);
342
+
343
+ if (cipher == NULL) {
344
+ return Qnil;
345
+ }
346
+
347
+ rb_str = rb_str_new2(cipher);
348
+ #ifdef HAVE_RUBY_ENCODING_H
349
+ rb_enc_associate(rb_str, rb_utf8_encoding());
350
+ #endif
351
+
352
+ return rb_str;
353
+ }
354
+
334
355
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
335
356
  struct nogvl_connect_args args;
336
357
  time_t start_time, end_time, elapsed_time, connect_timeout;
@@ -372,7 +393,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
372
393
  if (wrapper->connect_timeout)
373
394
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
374
395
  if (rv == Qfalse)
375
- return rb_raise_mysql2_error(wrapper);
396
+ rb_raise_mysql2_error(wrapper);
376
397
  }
377
398
 
378
399
  wrapper->server_version = mysql_get_server_version(wrapper->client);
@@ -381,13 +402,12 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
381
402
  }
382
403
 
383
404
  /*
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.
405
+ * Immediately disconnect from the server; normally the garbage collector
406
+ * will disconnect automatically when a connection is no longer needed.
407
+ * Explicitly closing this will free up server resources sooner than waiting
408
+ * for the garbage collector.
388
409
  *
389
- * @see http://dev.mysql.com/doc/en/communication-errors.html
390
- * @return [void]
410
+ * @return [nil]
391
411
  */
392
412
  static VALUE rb_mysql_client_close(VALUE self) {
393
413
  GET_CLIENT(self);
@@ -418,8 +438,8 @@ static VALUE do_send_query(void *args) {
418
438
  mysql_client_wrapper *wrapper = query_args->wrapper;
419
439
  if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
420
440
  /* an error occurred, we're not active anymore */
421
- MARK_CONN_INACTIVE(self);
422
- return rb_raise_mysql2_error(wrapper);
441
+ wrapper->active_thread = Qnil;
442
+ rb_raise_mysql2_error(wrapper);
423
443
  }
424
444
  return Qnil;
425
445
  }
@@ -448,7 +468,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
448
468
 
449
469
  /* once our result is stored off, this connection is
450
470
  ready for another command to be issued */
451
- MARK_CONN_INACTIVE(self);
471
+ wrapper->active_thread = Qnil;
452
472
 
453
473
  return result;
454
474
  }
@@ -480,8 +500,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
480
500
  REQUIRE_CONNECTED(wrapper);
481
501
  if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
482
502
  /* an error occurred, mark this connection inactive */
483
- MARK_CONN_INACTIVE(self);
484
- return rb_raise_mysql2_error(wrapper);
503
+ wrapper->active_thread = Qnil;
504
+ rb_raise_mysql2_error(wrapper);
485
505
  }
486
506
 
487
507
  is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
@@ -493,7 +513,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
493
513
 
494
514
  if (result == NULL) {
495
515
  if (mysql_errno(wrapper->client) != 0) {
496
- MARK_CONN_INACTIVE(self);
516
+ wrapper->active_thread = Qnil;
497
517
  rb_raise_mysql2_error(wrapper);
498
518
  }
499
519
  /* no data and no error, so query was not a SELECT */
@@ -517,7 +537,7 @@ struct async_query_args {
517
537
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
518
538
  GET_CLIENT(self);
519
539
 
520
- MARK_CONN_INACTIVE(self);
540
+ wrapper->active_thread = Qnil;
521
541
  wrapper->connected = 0;
522
542
 
523
543
  /* Invalidate the MySQL socket to prevent further communication.
@@ -588,7 +608,7 @@ static VALUE finish_and_mark_inactive(void *args) {
588
608
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
589
609
  mysql_free_result(result);
590
610
 
591
- MARK_CONN_INACTIVE(self);
611
+ wrapper->active_thread = Qnil;
592
612
  }
593
613
 
594
614
  return Qnil;
@@ -1011,10 +1031,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1011
1031
  static VALUE rb_mysql_client_more_results(VALUE self)
1012
1032
  {
1013
1033
  GET_CLIENT(self);
1014
- if (mysql_more_results(wrapper->client) == 0)
1015
- return Qfalse;
1016
- else
1017
- return Qtrue;
1034
+ if (mysql_more_results(wrapper->client) == 0)
1035
+ return Qfalse;
1036
+ else
1037
+ return Qtrue;
1018
1038
  }
1019
1039
 
1020
1040
  /* call-seq:
@@ -1081,6 +1101,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1081
1101
  }
1082
1102
  #endif
1083
1103
 
1104
+ /* call-seq:
1105
+ * client.automatic_close?
1106
+ *
1107
+ * @return [Boolean]
1108
+ */
1109
+ static VALUE get_automatic_close(VALUE self) {
1110
+ GET_CLIENT(self);
1111
+ return wrapper->automatic_close ? Qtrue : Qfalse;
1112
+ }
1113
+
1114
+ /* call-seq:
1115
+ * client.automatic_close = false
1116
+ *
1117
+ * Set this to +false+ to leave the connection open after it is garbage
1118
+ * collected. To avoid "Aborted connection" errors on the server, explicitly
1119
+ * call +close+ when the connection is no longer needed.
1120
+ *
1121
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
1122
+ */
1123
+ static VALUE set_automatic_close(VALUE self, VALUE value) {
1124
+ GET_CLIENT(self);
1125
+ if (RTEST(value)) {
1126
+ wrapper->automatic_close = 1;
1127
+ } else {
1128
+ #ifndef _WIN32
1129
+ wrapper->automatic_close = 0;
1130
+ #else
1131
+ rb_warn("Connections are always closed by garbage collector on Windows");
1132
+ #endif
1133
+ }
1134
+ return value;
1135
+ }
1136
+
1084
1137
  /* call-seq:
1085
1138
  * client.reconnect = true
1086
1139
  *
@@ -1195,7 +1248,7 @@ static VALUE initialize_ext(VALUE self) {
1195
1248
 
1196
1249
  if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
1197
1250
  /* TODO: warning - not enough memory? */
1198
- return rb_raise_mysql2_error(wrapper);
1251
+ rb_raise_mysql2_error(wrapper);
1199
1252
  }
1200
1253
 
1201
1254
  wrapper->initialized = 1;
@@ -1264,9 +1317,12 @@ void init_mysql2_client() {
1264
1317
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1265
1318
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1266
1319
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
1320
+ rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
1321
+ rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
1267
1322
  rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
1268
1323
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1269
1324
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1325
+ rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1270
1326
  #ifdef HAVE_RUBY_ENCODING_H
1271
1327
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1272
1328
  #endif
@@ -43,6 +43,7 @@ typedef struct {
43
43
  int reconnect_enabled;
44
44
  unsigned int connect_timeout;
45
45
  int active;
46
+ int automatic_close;
46
47
  int connected;
47
48
  int initialized;
48
49
  int refcount;
@@ -58,10 +59,6 @@ typedef struct {
58
59
 
59
60
  void rb_mysql_client_set_active_thread(VALUE self);
60
61
 
61
- #define MARK_CONN_INACTIVE(conn) do {\
62
- wrapper->active_thread = Qnil; \
63
- } while(0)
64
-
65
62
  #define GET_CLIENT(self) \
66
63
  mysql_client_wrapper *wrapper; \
67
64
  Data_Get_Struct(self, mysql_client_wrapper, wrapper);
@@ -104,6 +104,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
104
104
  wrapper->stmt_wrapper->stmt->bind_result_done = 0;
105
105
  }
106
106
 
107
+ if (wrapper->statement != Qnil) {
108
+ decr_mysql2_stmt(wrapper->stmt_wrapper);
109
+ }
110
+
107
111
  if (wrapper->result_buffers) {
108
112
  unsigned int i;
109
113
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -136,13 +140,15 @@ static void rb_mysql_result_free(void *ptr) {
136
140
  decr_mysql2_client(wrapper->client_wrapper);
137
141
  }
138
142
 
139
- if (wrapper->statement != Qnil) {
140
- decr_mysql2_stmt(wrapper->stmt_wrapper);
141
- }
142
-
143
143
  xfree(wrapper);
144
144
  }
145
145
 
146
+ static VALUE rb_mysql_result_free_(VALUE self) {
147
+ GET_RESULT(self);
148
+ rb_mysql_result_free_result(wrapper);
149
+ return Qnil;
150
+ }
151
+
146
152
  /*
147
153
  * for small results, this won't hit the network, but there's no
148
154
  * reliable way for us to tell this so we'll always release the GVL
@@ -511,7 +517,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
511
517
  return rowVal;
512
518
  }
513
519
 
514
-
515
520
  static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
516
521
  {
517
522
  VALUE rowVal;
@@ -911,6 +916,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
911
916
  if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
912
917
  wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
913
918
  if (wrapper->numberOfRows == 0) {
919
+ rb_mysql_result_free_result(wrapper);
914
920
  wrapper->rows = rb_ary_new();
915
921
  return wrapper->rows;
916
922
  }
@@ -1007,6 +1013,7 @@ void init_mysql2_result() {
1007
1013
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1008
1014
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
1009
1015
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1016
+ rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
1010
1017
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
1011
1018
  rb_define_alias(cMysql2Result, "size", "count");
1012
1019
 
@@ -3,7 +3,7 @@
3
3
  VALUE cMysql2Statement;
4
4
  extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
5
5
  static VALUE sym_stream, intern_new_with_args, intern_each;
6
- static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
6
+ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year, intern_to_s;
7
7
 
8
8
  #define GET_STATEMENT(self) \
9
9
  mysql_stmt_wrapper *stmt_wrapper; \
@@ -11,7 +11,6 @@ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, inter
11
11
  if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
12
12
  if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
13
13
 
14
-
15
14
  static void rb_mysql_stmt_mark(void * ptr) {
16
15
  mysql_stmt_wrapper *stmt_wrapper = ptr;
17
16
  if (!stmt_wrapper) return;
@@ -42,7 +41,6 @@ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
42
41
  }
43
42
  }
44
43
 
45
-
46
44
  void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
47
45
  VALUE e;
48
46
  GET_CLIENT(stmt_wrapper->client);
@@ -71,7 +69,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
71
69
  rb_exc_raise(e);
72
70
  }
73
71
 
74
-
75
72
  /*
76
73
  * used to pass all arguments to mysql_stmt_prepare while inside
77
74
  * nogvl_prepare_statement_args
@@ -180,14 +177,17 @@ static void *nogvl_execute(void *ptr) {
180
177
  }
181
178
  }
182
179
 
183
- static void *nogvl_stmt_store_result(void *ptr) {
184
- MYSQL_STMT *stmt = ptr;
180
+ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
181
+ unsigned long length;
185
182
 
186
- if (mysql_stmt_store_result(stmt)) {
187
- return (void *)Qfalse;
188
- } else {
189
- return (void *)Qtrue;
190
- }
183
+ bind_buffer->buffer_type = MYSQL_TYPE_STRING;
184
+ bind_buffer->buffer = RSTRING_PTR(string);
185
+
186
+ length = RSTRING_LEN(string);
187
+ bind_buffer->buffer_length = length;
188
+ *length_buffer = length;
189
+
190
+ bind_buffer->length = length_buffer;
191
191
  }
192
192
 
193
193
  /* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means
@@ -280,11 +280,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
280
280
  #ifdef HAVE_RUBY_ENCODING_H
281
281
  params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
282
282
  #endif
283
- bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
284
- bind_buffers[i].buffer = RSTRING_PTR(params_enc[i]);
285
- bind_buffers[i].buffer_length = RSTRING_LEN(params_enc[i]);
286
- length_buffers[i] = bind_buffers[i].buffer_length;
287
- bind_buffers[i].length = &length_buffers[i];
283
+ set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
288
284
  }
289
285
  break;
290
286
  default:
@@ -324,6 +320,19 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
324
320
  *(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
325
321
  } else if (CLASS_OF(argv[i]) == cBigDecimal) {
326
322
  bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
323
+
324
+ // DECIMAL are represented with the "string representation of the
325
+ // original server-side value", see
326
+ // https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html
327
+ // This should be independent of the locale used both on the server
328
+ // and the client side.
329
+ VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
330
+
331
+ params_enc[i] = rb_val_as_string;
332
+ #ifdef HAVE_RUBY_ENCODING_H
333
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
334
+ #endif
335
+ set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
327
336
  }
328
337
  break;
329
338
  }
@@ -347,8 +356,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
347
356
  if (metadata == NULL) {
348
357
  if (mysql_stmt_errno(stmt) != 0) {
349
358
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
350
-
351
- MARK_CONN_INACTIVE(stmt_wrapper->client);
359
+ wrapper->active_thread = Qnil;
352
360
  rb_raise_mysql2_stmt_error(stmt_wrapper);
353
361
  }
354
362
  // no data and no error, so query was not a SELECT
@@ -362,11 +370,11 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
362
370
  is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
363
371
  if (!is_streaming) {
364
372
  // recieve the whole result set from the server
365
- if (rb_thread_call_without_gvl(nogvl_stmt_store_result, stmt, RUBY_UBF_IO, 0) == Qfalse) {
373
+ if (mysql_stmt_store_result(stmt)) {
366
374
  mysql_free_result(metadata);
367
375
  rb_raise_mysql2_stmt_error(stmt_wrapper);
368
376
  }
369
- MARK_CONN_INACTIVE(stmt_wrapper->client);
377
+ wrapper->active_thread = Qnil;
370
378
  }
371
379
 
372
380
  resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
@@ -491,4 +499,6 @@ void init_mysql2_statement() {
491
499
  intern_day = rb_intern("day");
492
500
  intern_month = rb_intern("month");
493
501
  intern_year = rb_intern("year");
502
+
503
+ intern_to_s = rb_intern("to_s");
494
504
  }
Binary file
Binary file
Binary file
Binary file
@@ -30,13 +30,13 @@ module Mysql2
30
30
  opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
31
31
 
32
32
  # TODO: stricter validation rather than silent massaging
33
- [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key|
33
+ [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
34
34
  next unless opts.key?(key)
35
35
  case key
36
- when :reconnect, :local_infile, :secure_auth
36
+ when :reconnect, :local_infile, :secure_auth, :automatic_close
37
37
  send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
38
38
  when :connect_timeout, :read_timeout, :write_timeout
39
- send(:"#{key}=", opts[key].to_i)
39
+ send(:"#{key}=", opts[key]) unless opts[key].nil?
40
40
  else
41
41
  send(:"#{key}=", opts[key])
42
42
  end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
  end
@@ -1,5 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  require 'spec_helper'
3
+ require 'stringio'
3
4
 
4
5
  RSpec.describe Mysql2::Client do
5
6
  context "using defaults file" do
@@ -145,16 +146,12 @@ RSpec.describe Mysql2::Client do
145
146
  # rubocop:enable Style/TrailingComma
146
147
  }.not_to raise_error
147
148
 
148
- results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
149
- expect(results[0]['Variable_name']).to eql('Ssl_cipher')
150
- expect(results[0]['Value']).not_to be_nil
151
- expect(results[0]['Value']).to be_an_instance_of(String)
152
- expect(results[0]['Value']).not_to be_empty
149
+ results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
150
+ expect(results['Ssl_cipher']).not_to be_empty
151
+ expect(results['Ssl_version']).not_to be_empty
153
152
 
154
- expect(results[1]['Variable_name']).to eql('Ssl_version')
155
- expect(results[1]['Value']).not_to be_nil
156
- expect(results[1]['Value']).to be_an_instance_of(String)
157
- expect(results[1]['Value']).not_to be_empty
153
+ expect(ssl_client.ssl_cipher).not_to be_empty
154
+ expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
158
155
 
159
156
  ssl_client.close
160
157
  end
@@ -172,48 +169,97 @@ RSpec.describe Mysql2::Client do
172
169
  expect {
173
170
  Mysql2::Client.new(DatabaseCredentials['root']).close
174
171
  }.to_not change {
175
- @client.query("SHOW STATUS LIKE 'Aborted_clients'").first['Value'].to_i
172
+ @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
173
+ @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
176
174
  }
177
175
  end
178
176
 
179
177
  it "should not leave dangling connections after garbage collection" do
180
178
  run_gc
179
+ expect {
180
+ expect {
181
+ 10.times do
182
+ Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
183
+ end
184
+ }.to change {
185
+ @client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
186
+ }.by(10)
181
187
 
182
- client = Mysql2::Client.new(DatabaseCredentials['root'])
183
- before_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
188
+ run_gc
189
+ }.to_not change {
190
+ @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
191
+ @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
192
+ }
193
+ end
184
194
 
185
- 10.times do
186
- Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
195
+ context "#automatic_close" do
196
+ it "is enabled by default" do
197
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
198
+ expect(client.automatic_close?).to be(true)
187
199
  end
188
- after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
189
- expect(after_count).to eq(before_count + 10)
190
200
 
191
- run_gc
192
- final_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
193
- expect(final_count).to eq(before_count)
194
- end
201
+ if RUBY_PLATFORM =~ /mingw|mswin/
202
+ it "cannot be disabled" do
203
+ stderr, $stderr = $stderr, StringIO.new
195
204
 
196
- it "should not close connections when running in a child process" do
197
- pending("fork is not available on this platform") unless Process.respond_to?(:fork)
205
+ begin
206
+ Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
207
+ expect($stderr.string).to include('always closed by garbage collector')
208
+ $stderr.reopen
198
209
 
199
- run_gc
200
- client = Mysql2::Client.new(DatabaseCredentials['root'])
210
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
211
+ client.automatic_close = false
212
+ expect($stderr.string).to include('always closed by garbage collector')
213
+ $stderr.reopen
214
+
215
+ expect { client.automatic_close = true }.to_not change { $stderr.string }
216
+ ensure
217
+ $stderr = stderr
218
+ end
219
+ end
220
+ else
221
+ it "can be configured" do
222
+ client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
223
+ expect(client.automatic_close?).to be(false)
224
+ end
201
225
 
202
- # this empty `fork` call fixes this tests on RBX; without it, the next
203
- # `fork` call hangs forever. WTF?
204
- fork {}
226
+ it "can be assigned" do
227
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
228
+ client.automatic_close = false
229
+ expect(client.automatic_close?).to be(false)
205
230
 
206
- fork do
207
- client.query('SELECT 1')
208
- client = nil
209
- run_gc
210
- end
231
+ client.automatic_close = true
232
+ expect(client.automatic_close?).to be(true)
233
+
234
+ client.automatic_close = nil
235
+ expect(client.automatic_close?).to be(false)
236
+
237
+ client.automatic_close = 9
238
+ expect(client.automatic_close?).to be(true)
239
+ end
240
+
241
+ it "should not close connections when running in a child process" do
242
+ run_gc
243
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
244
+ client.automatic_close = false
211
245
 
212
- Process.wait
246
+ # this empty `fork` call fixes this tests on RBX; without it, the next
247
+ # `fork` call hangs forever. WTF?
248
+ fork {}
213
249
 
214
- # this will throw an error if the underlying socket was shutdown by the
215
- # child's GC
216
- expect { client.query('SELECT 1') }.to_not raise_exception
250
+ fork do
251
+ client.query('SELECT 1')
252
+ client = nil
253
+ run_gc
254
+ end
255
+
256
+ Process.wait
257
+
258
+ # this will throw an error if the underlying socket was shutdown by the
259
+ # child's GC
260
+ expect { client.query('SELECT 1') }.to_not raise_exception
261
+ end
262
+ end
217
263
  end
218
264
 
219
265
  it "should be able to connect to database with numeric-only name" do
@@ -352,6 +398,12 @@ RSpec.describe Mysql2::Client do
352
398
  }.to raise_error(Mysql2::Error)
353
399
  end
354
400
 
401
+ it "should allow nil read_timeout" do
402
+ client = Mysql2::Client.new(:read_timeout => nil)
403
+
404
+ expect(client.read_timeout).to be_nil
405
+ end
406
+
355
407
  context "#query" do
356
408
  it "should let you query again if iterating is finished when streaming" do
357
409
  @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a
@@ -22,6 +22,10 @@ RSpec.describe Mysql2::Result do
22
22
  expect(@result).to respond_to(:each)
23
23
  end
24
24
 
25
+ it "should respond to #free" do
26
+ expect(@result).to respond_to(:free)
27
+ end
28
+
25
29
  it "should raise a Mysql2::Error exception upon a bad query" do
26
30
  expect {
27
31
  @client.query "bad sql"
@@ -117,6 +117,17 @@ RSpec.describe Mysql2::Statement do
117
117
  expect(list[1]).to eq('2')
118
118
  end
119
119
 
120
+ it "should update a DECIMAL value passing a BigDecimal" do
121
+ @client.query 'USE test'
122
+ @client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
123
+ @client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))'
124
+
125
+ @client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal.new("123.45"))
126
+
127
+ test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first
128
+ expect(test_result['decimal_test']).to eql(123.45)
129
+ end
130
+
120
131
  context "utf8_db" do
121
132
  before(:each) do
122
133
  @client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mysql2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: x64-mingw32
6
6
  authors:
7
7
  - Brian Lopez
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-11-25 00:00:00.000000000 Z
12
+ date: 2016-02-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email:
@@ -42,6 +42,7 @@ files:
42
42
  - lib/mysql2/2.0/mysql2.so
43
43
  - lib/mysql2/2.1/mysql2.so
44
44
  - lib/mysql2/2.2/mysql2.so
45
+ - lib/mysql2/2.3/mysql2.so
45
46
  - lib/mysql2/client.rb
46
47
  - lib/mysql2/console.rb
47
48
  - lib/mysql2/em.rb
@@ -114,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
115
  version: '0'
115
116
  requirements: []
116
117
  rubyforge_project:
117
- rubygems_version: 2.4.8
118
+ rubygems_version: 2.5.1
118
119
  signing_key:
119
120
  specification_version: 4
120
121
  summary: A simple, fast Mysql library for Ruby, binding to libmysql