mysql2 0.4.2 → 0.4.3

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: bfe50d56c4b02379320402f6c9a50e1c004855b1
4
- data.tar.gz: 495a7a40b1a1fc006e8f3816b0b215c8a82f4585
3
+ metadata.gz: 3a4ccc20c41020f497f28c56fe825ca41349ee9c
4
+ data.tar.gz: 7ea3444599e0c9d8b0efb610d9b7fa408aa355c7
5
5
  SHA512:
6
- metadata.gz: 076e0ae48e790418cc2272bf14f3f93859b751452eeaaefd43537a22266bf5cab6d443645b06d2e3bcaab3be2768e65bc5b699bb5e100e10e5a35911d4e96c33
7
- data.tar.gz: b921cb3b96ef7428db4870ca82719a52b7921d24fed356ba6765ff1890a98ae8c0c1351e8c158fa098d8652aebf4b958ec1cb3eeb46ff2e8c2b9e58d69dde1d7
6
+ metadata.gz: 3dc8dd2dfa209530b8793abf02476682d01cdbe44d6a847421b90fdaf38150a1ac33a7f9ba1db1df07398e22bfdfb27e1ad23aa2d0f4e1640a9169b3db683321
7
+ data.tar.gz: 107cc184c12df06949fa54851673c96c977ced271383f52e03b1bb714dab7a06c0211a0110202a9f397703f3d55bb70d2c7420a537b286c5fa5ba890be9dc578
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
  }
@@ -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: ruby
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: