mysql2 0.4.6 → 0.4.7

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: 0f6e6398c4050512cbe22c6994153fb0ab4b3d89
4
- data.tar.gz: 8e6345e8df7b550c7248f86cb374422866ffdd38
3
+ metadata.gz: 49904d19056ae4c90f12a539d56dfc3789fd8bc8
4
+ data.tar.gz: a9c541dd2693bc2bce4eba0a81654e0cefd2d704
5
5
  SHA512:
6
- metadata.gz: 5b820a05878ea935e6a25b4b1e34bfebaf47fc48a8ef24ff8f8920cc4ecaabfc2f0ba7cd590c8d4c51632d5e564fba11ae35880e0bf3230425a7264341e2625a
7
- data.tar.gz: 32f8d9a642c643225386883f97305be79868984a55a541358983ab70ec07f421dc999e8a656f51044c089a2aecb697d2b217453cf2ce0ca9fe4a17f313131e8e
6
+ metadata.gz: 6648cd5f75da53d33a4d2630d138264974bc8bf902b68ebe988a261a6ed7c4e91ad21939153b3bd78a2cd5202a26f03dfac3834a0fc074464aeef61ea8417c45
7
+ data.tar.gz: 76e9e4b9a2d35d87011de33426c88600b070b5cc600aede7e522d9a5f43186d3e19421f8ee540a52a265d1e19d25d6bdbb4bd1732e71ff234e3d634cdf0f99f4
data/README.md CHANGED
@@ -112,7 +112,7 @@ Connect to a database:
112
112
  ``` ruby
113
113
  # this takes a hash of options, almost all of which map directly
114
114
  # to the familiar database.yml in rails
115
- # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html
115
+ # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html
116
116
  client = Mysql2::Client.new(:host => "localhost", :username => "root")
117
117
  ```
118
118
 
@@ -213,6 +213,21 @@ Mysql2::Client.new(
213
213
  )
214
214
  ```
215
215
 
216
+ ### Connecting to localhost
217
+
218
+ The underlying MySQL client library has a special interpretation of the "localhost" default connection host name:
219
+
220
+ 1. Attempt to connect via local socket (as specified in your distribution's my.cnf or `default_file` config file).
221
+ 2. But if the socket is not available, look up "localhost" to find an IP address...
222
+ * The file "/etc/hosts" is generally the source of truth for "localhost", and can override its value.
223
+ * Systems with IPv4 use "127.0.0.1"
224
+ * Systems with IPv6 use "::1"
225
+ * Systems with both IPv4 and IPv6 can be configured to prefer one or the other.
226
+ 3. If an address is found for "localhost", connect to it over TCP/IP.
227
+
228
+ You can be explicit about using either a local socket or a TCP/IP connection,
229
+ and whether to use IPv4 or IPv6, by specifying a value other than "localhost"
230
+ for the `host` or `socket` connection options.
216
231
 
217
232
  ### SSL options
218
233
 
@@ -235,47 +250,6 @@ Mysql2::Client.new(
235
250
  )
236
251
  ```
237
252
 
238
- ### Multiple result sets
239
-
240
- You can also retrieve multiple result sets. For this to work you need to
241
- connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
242
- be used with stored procedures that return more than one result set, and for
243
- bundling several SQL statements into a single call to `client.query`.
244
-
245
- ``` ruby
246
- client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
247
- result = client.query('CALL sp_customer_list( 25, 10 )')
248
- # result now contains the first result set
249
- while client.next_result
250
- result = client.store_result
251
- # result now contains the next result set
252
- end
253
- ```
254
-
255
- Repeated calls to `client.next_result` will return true, false, or raise an
256
- exception if the respective query erred. When `client.next_result` returns true,
257
- call `client.store_result` to retrieve a result object. Exceptions are not
258
- raised until `client.next_result` is called to find the status of the respective
259
- query. Subsequent queries are not executed if an earlier query raised an
260
- exception. Subsequent calls to `client.next_result` will return false.
261
-
262
- ``` ruby
263
- result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
264
- p result.first
265
-
266
- while client.next_result
267
- result = client.store_result
268
- p result.first
269
- end
270
- ```
271
-
272
- Yields:
273
- ```
274
- {"1"=>1}
275
- {"2"=>2}
276
- next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
277
- ```
278
-
279
253
  ### Secure auth
280
254
 
281
255
  Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
@@ -332,6 +306,47 @@ It is useful if you want to provide session options which survive reconnection.
332
306
  Mysql2::Client.new(:init_command => "SET @@SESSION.sql_mode = 'STRICT_ALL_TABLES'")
333
307
  ```
334
308
 
309
+ ### Multiple result sets
310
+
311
+ You can also retrieve multiple result sets. For this to work you need to
312
+ connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
313
+ be used with stored procedures that return more than one result set, and for
314
+ bundling several SQL statements into a single call to `client.query`.
315
+
316
+ ``` ruby
317
+ client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
318
+ result = client.query('CALL sp_customer_list( 25, 10 )')
319
+ # result now contains the first result set
320
+ while client.next_result
321
+ result = client.store_result
322
+ # result now contains the next result set
323
+ end
324
+ ```
325
+
326
+ Repeated calls to `client.next_result` will return true, false, or raise an
327
+ exception if the respective query erred. When `client.next_result` returns true,
328
+ call `client.store_result` to retrieve a result object. Exceptions are not
329
+ raised until `client.next_result` is called to find the status of the respective
330
+ query. Subsequent queries are not executed if an earlier query raised an
331
+ exception. Subsequent calls to `client.next_result` will return false.
332
+
333
+ ``` ruby
334
+ result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
335
+ p result.first
336
+
337
+ while client.next_result
338
+ result = client.store_result
339
+ p result.first
340
+ end
341
+ ```
342
+
343
+ Yields:
344
+ ```
345
+ {"1"=>1}
346
+ {"2"=>2}
347
+ next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
348
+ ```
349
+
335
350
  ## Cascading config
336
351
 
337
352
  The default config hash is at:
@@ -30,15 +30,21 @@ VALUE rb_hash_dup(VALUE other) {
30
30
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
31
  }
32
32
 
33
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
34
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
35
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
36
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
37
+ #endif
38
+
33
39
  #define REQUIRE_CONNECTED(wrapper) \
34
40
  REQUIRE_INITIALIZED(wrapper) \
35
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
41
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
36
42
  rb_raise(cMysql2Error, "MySQL client is not connected"); \
37
43
  }
38
44
 
39
45
  #define REQUIRE_NOT_CONNECTED(wrapper) \
40
46
  REQUIRE_INITIALIZED(wrapper) \
41
- if (wrapper->connected) { \
47
+ if (CONNECTED(wrapper)) { \
42
48
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
43
49
  }
44
50
 
@@ -107,14 +113,13 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
107
113
  return Qnil;
108
114
  }
109
115
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
110
- GET_CLIENT(self);
116
+ GET_CLIENT(self);
111
117
  int val = NUM2INT( setting );
112
118
  if (version >= 50703 && version < 50711) {
113
119
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
114
120
  bool b = ( val == SSL_MODE_REQUIRED );
115
121
  int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
116
122
  return INT2NUM(result);
117
-
118
123
  } else {
119
124
  rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
120
125
  return Qnil;
@@ -122,7 +127,7 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
122
127
  }
123
128
  #endif
124
129
  #ifdef FULL_SSL_MODE_SUPPORT
125
- GET_CLIENT(self);
130
+ GET_CLIENT(self);
126
131
  int val = NUM2INT( setting );
127
132
 
128
133
  if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
@@ -264,11 +269,10 @@ static VALUE invalidate_fd(int clientfd)
264
269
  static void *nogvl_close(void *ptr) {
265
270
  mysql_client_wrapper *wrapper = ptr;
266
271
 
267
- if (wrapper->client) {
272
+ if (!wrapper->closed) {
268
273
  mysql_close(wrapper->client);
269
- xfree(wrapper->client);
270
- wrapper->client = NULL;
271
- wrapper->connected = 0;
274
+ wrapper->closed = 1;
275
+ wrapper->reconnect_enabled = 0;
272
276
  wrapper->active_thread = Qnil;
273
277
  }
274
278
 
@@ -287,7 +291,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
287
291
 
288
292
  if (wrapper->refcount == 0) {
289
293
  #ifndef _WIN32
290
- if (wrapper->connected && !wrapper->automatic_close) {
294
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
291
295
  /* The client is being garbage collected while connected. Prevent
292
296
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
293
297
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -299,10 +303,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
299
303
  fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
300
304
  close(wrapper->client->net.fd);
301
305
  }
306
+ wrapper->client->net.fd = -1;
302
307
  }
303
308
  #endif
304
309
 
305
310
  nogvl_close(wrapper);
311
+ xfree(wrapper->client);
306
312
  xfree(wrapper);
307
313
  }
308
314
  }
@@ -317,9 +323,9 @@ static VALUE allocate(VALUE klass) {
317
323
  wrapper->server_version = 0;
318
324
  wrapper->reconnect_enabled = 0;
319
325
  wrapper->connect_timeout = 0;
320
- wrapper->connected = 0; /* means that a database connection is open */
321
326
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
322
327
  wrapper->refcount = 1;
328
+ wrapper->closed = 0;
323
329
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
324
330
 
325
331
  return obj;
@@ -450,7 +456,6 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
450
456
  }
451
457
 
452
458
  wrapper->server_version = mysql_get_server_version(wrapper->client);
453
- wrapper->connected = 1;
454
459
  return self;
455
460
  }
456
461
 
@@ -465,13 +470,23 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
465
470
  static VALUE rb_mysql_client_close(VALUE self) {
466
471
  GET_CLIENT(self);
467
472
 
468
- if (wrapper->connected) {
473
+ if (wrapper->client) {
469
474
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
470
475
  }
471
476
 
472
477
  return Qnil;
473
478
  }
474
479
 
480
+ /* call-seq:
481
+ * client.closed?
482
+ *
483
+ * @return [Boolean]
484
+ */
485
+ static VALUE rb_mysql_client_closed(VALUE self) {
486
+ GET_CLIENT(self);
487
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
488
+ }
489
+
475
490
  /*
476
491
  * mysql_send_query is unlikely to block since most queries are small
477
492
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -591,16 +606,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
591
606
  GET_CLIENT(self);
592
607
 
593
608
  wrapper->active_thread = Qnil;
594
- wrapper->connected = 0;
595
609
 
596
610
  /* Invalidate the MySQL socket to prevent further communication.
597
611
  * The GC will come along later and call mysql_close to free it.
598
612
  */
599
- if (wrapper->client) {
613
+ if (CONNECTED(wrapper)) {
600
614
  if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
601
615
  fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
602
616
  close(wrapper->client->net.fd);
603
617
  }
618
+ wrapper->client->net.fd = -1;
604
619
  }
605
620
 
606
621
  rb_exc_raise(error);
@@ -656,19 +671,21 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
656
671
 
657
672
  /* Check if execution terminated while result was still being read. */
658
673
  if (!NIL_P(wrapper->active_thread)) {
659
- /* Invalidate the MySQL socket to prevent further communication. */
674
+ if (CONNECTED(wrapper)) {
675
+ /* Invalidate the MySQL socket to prevent further communication. */
660
676
  #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
- }
677
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
678
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
679
+ close(wrapper->client->net.fd);
680
+ }
665
681
  #else
666
- close(wrapper->client->net.fd);
682
+ close(wrapper->client->net.fd);
667
683
  #endif
684
+ wrapper->client->net.fd = -1;
685
+ }
668
686
  /* Skip mysql client check performed before command execution. */
669
687
  wrapper->client->status = MYSQL_STATUS_READY;
670
688
  wrapper->active_thread = Qnil;
671
- wrapper->connected = 0;
672
689
  }
673
690
 
674
691
  return Qnil;
@@ -886,6 +903,11 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
886
903
  retval = charval;
887
904
  break;
888
905
 
906
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
907
+ boolval = (value == Qfalse ? 0 : 1);
908
+ retval = &boolval;
909
+ break;
910
+
889
911
  default:
890
912
  return Qfalse;
891
913
  }
@@ -1075,7 +1097,7 @@ static void *nogvl_ping(void *ptr) {
1075
1097
  static VALUE rb_mysql_client_ping(VALUE self) {
1076
1098
  GET_CLIENT(self);
1077
1099
 
1078
- if (!wrapper->connected) {
1100
+ if (!CONNECTED(wrapper)) {
1079
1101
  return Qfalse;
1080
1102
  } else {
1081
1103
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
@@ -1303,6 +1325,10 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1303
1325
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1304
1326
  }
1305
1327
 
1328
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1329
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1330
+ }
1331
+
1306
1332
  static VALUE initialize_ext(VALUE self) {
1307
1333
  GET_CLIENT(self);
1308
1334
 
@@ -1363,6 +1389,7 @@ void init_mysql2_client() {
1363
1389
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1364
1390
 
1365
1391
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1392
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1366
1393
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1367
1394
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1368
1395
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1398,6 +1425,7 @@ void init_mysql2_client() {
1398
1425
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1399
1426
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1400
1427
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1428
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1401
1429
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1402
1430
  rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1403
1431
  rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
@@ -1419,6 +1447,10 @@ void init_mysql2_client() {
1419
1447
  #ifdef CLIENT_LONG_PASSWORD
1420
1448
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1421
1449
  LONG2NUM(CLIENT_LONG_PASSWORD));
1450
+ #else
1451
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1452
+ * but we're using it in our default connection flags. */
1453
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1422
1454
  #endif
1423
1455
 
1424
1456
  #ifdef CLIENT_FOUND_ROWS
@@ -44,10 +44,9 @@ typedef struct {
44
44
  unsigned int connect_timeout;
45
45
  int active;
46
46
  int automatic_close;
47
- int connected;
48
47
  int initialized;
49
48
  int refcount;
50
- int freed;
49
+ int closed;
51
50
  MYSQL *client;
52
51
  } mysql_client_wrapper;
53
52
 
@@ -105,13 +105,16 @@ else
105
105
  asplode 'mysql.h'
106
106
  end
107
107
 
108
- add_ssl_defines([prefix, 'mysql.h'].compact.join('/'))
109
-
110
108
  %w(errmsg.h mysqld_error.h).each do |h|
111
109
  header = [prefix, h].compact.join '/'
112
110
  asplode h unless have_header header
113
111
  end
114
112
 
113
+ mysql_h = [prefix, 'mysql.h'].compact.join('/')
114
+ add_ssl_defines(mysql_h)
115
+ have_struct_member('MYSQL', 'net.vio', mysql_h)
116
+ have_struct_member('MYSQL', 'net.pvio', mysql_h)
117
+
115
118
  # This is our wishlist. We use whichever flags work on the host.
116
119
  # -Wall and -Wextra are included by default.
117
120
  wishlist = [
@@ -14,11 +14,13 @@ void Init_mysql2(void);
14
14
  #include <mysql_com.h>
15
15
  #include <errmsg.h>
16
16
  #include <mysqld_error.h>
17
+ #include <mysql_version.h>
17
18
  #else
18
19
  #include <mysql/mysql.h>
19
20
  #include <mysql/mysql_com.h>
20
21
  #include <mysql/errmsg.h>
21
22
  #include <mysql/mysqld_error.h>
23
+ #include <mysql/mysql_version.h>
22
24
  #endif
23
25
 
24
26
  #ifdef HAVE_RUBY_ENCODING_H
@@ -468,6 +468,7 @@ static VALUE fields(VALUE self) {
468
468
  rb_encoding *default_internal_enc, *conn_enc;
469
469
  #endif
470
470
  GET_STATEMENT(self);
471
+ GET_CLIENT(stmt_wrapper->client);
471
472
  stmt = stmt_wrapper->stmt;
472
473
 
473
474
  #ifdef HAVE_RUBY_ENCODING_H
@@ -478,12 +479,22 @@ static VALUE fields(VALUE self) {
478
479
  }
479
480
  #endif
480
481
 
481
- metadata = mysql_stmt_result_metadata(stmt);
482
+ metadata = mysql_stmt_result_metadata(stmt);
483
+ if (metadata == NULL) {
484
+ if (mysql_stmt_errno(stmt) != 0) {
485
+ // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
486
+ wrapper->active_thread = Qnil;
487
+ rb_raise_mysql2_stmt_error(stmt_wrapper);
488
+ }
489
+ // no data and no error, so query was not a SELECT
490
+ return Qnil;
491
+ }
492
+
482
493
  fields = mysql_fetch_fields(metadata);
483
494
  field_count = mysql_stmt_field_count(stmt);
484
495
  field_list = rb_ary_new2((long)field_count);
485
496
 
486
- for(i = 0; i < field_count; i++) {
497
+ for (i = 0; i < field_count; i++) {
487
498
  VALUE rb_field;
488
499
 
489
500
  rb_field = rb_str_new(fields[i].name, fields[i].name_length);
@@ -31,10 +31,10 @@ module Mysql2
31
31
  opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
32
32
 
33
33
  # TODO: stricter validation rather than silent massaging
34
- [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
34
+ [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close, :enable_cleartext_plugin].each do |key|
35
35
  next unless opts.key?(key)
36
36
  case key
37
- when :reconnect, :local_infile, :secure_auth, :automatic_close
37
+ when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
38
38
  send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
39
39
  when :connect_timeout, :read_timeout, :write_timeout
40
40
  send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
@@ -66,7 +66,7 @@ module Mysql2
66
66
 
67
67
  if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) }
68
68
  warn "============= WARNING FROM mysql2 ============="
69
- warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be deprecated at some point in the future."
69
+ warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future."
70
70
  warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options."
71
71
  warn "============= END WARNING FROM mysql2 ========="
72
72
  end
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.4.6"
2
+ VERSION = "0.4.7"
3
3
  end
@@ -70,6 +70,7 @@ begin
70
70
  let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
71
71
  let(:error) { StandardError.new('some error') }
72
72
  before { allow(client).to receive(:async_result).and_raise(error) }
73
+ after { client.close }
73
74
 
74
75
  it "should swallow exceptions raised in by the client" do
75
76
  errors = []
@@ -7,14 +7,13 @@ RSpec.describe Mysql2::Client do
7
7
 
8
8
  it "should not raise an exception for valid defaults group" do
9
9
  expect {
10
- opts = DatabaseCredentials['root'].merge(:default_file => cnf_file, :default_group => "test")
11
- @client = Mysql2::Client.new(opts)
10
+ new_client(:default_file => cnf_file, :default_group => "test")
12
11
  }.not_to raise_error
13
12
  end
14
13
 
15
14
  it "should not raise an exception without default group" do
16
15
  expect {
17
- @client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:default_file => cnf_file))
16
+ new_client(:default_file => cnf_file)
18
17
  }.not_to raise_error
19
18
  end
20
19
  end
@@ -23,29 +22,29 @@ RSpec.describe Mysql2::Client do
23
22
  expect {
24
23
  # The odd local host IP address forces the mysql client library to
25
24
  # use a TCP socket rather than a domain socket.
26
- Mysql2::Client.new DatabaseCredentials['root'].merge('host' => '127.0.0.2', 'port' => 999999)
25
+ new_client('host' => '127.0.0.2', 'port' => 999999)
27
26
  }.to raise_error(Mysql2::Error)
28
27
  end
29
28
 
30
29
  it "should raise an exception on create for invalid encodings" do
31
30
  expect {
32
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "fake"))
31
+ new_client(:encoding => "fake")
33
32
  }.to raise_error(Mysql2::Error)
34
33
  end
35
34
 
36
35
  it "should raise an exception on non-string encodings" do
37
36
  expect {
38
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => :fake))
37
+ new_client(:encoding => :fake)
39
38
  }.to raise_error(TypeError)
40
39
  end
41
40
 
42
41
  it "should not raise an exception on create for a valid encoding" do
43
42
  expect {
44
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
43
+ new_client(:encoding => "utf8")
45
44
  }.not_to raise_error
46
45
 
47
46
  expect {
48
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "big5"))
47
+ new_client(DatabaseCredentials['root'].merge(:encoding => "big5"))
49
48
  }.not_to raise_error
50
49
  end
51
50
 
@@ -88,7 +87,7 @@ RSpec.describe Mysql2::Client do
88
87
  it "should execute init command" do
89
88
  options = DatabaseCredentials['root'].dup
90
89
  options[:init_command] = "SET @something = 'setting_value';"
91
- client = Mysql2::Client.new(options)
90
+ client = new_client(options)
92
91
  result = client.query("SELECT @something;")
93
92
  expect(result.first['@something']).to eq('setting_value')
94
93
  end
@@ -97,7 +96,7 @@ RSpec.describe Mysql2::Client do
97
96
  options = DatabaseCredentials['root'].dup
98
97
  options[:init_command] = "SET @something = 'setting_value';"
99
98
  options[:reconnect] = true
100
- client = Mysql2::Client.new(options)
99
+ client = new_client(options)
101
100
 
102
101
  result = client.query("SELECT @something;")
103
102
  expect(result.first['@something']).to eq('setting_value')
@@ -138,15 +137,13 @@ RSpec.describe Mysql2::Client do
138
137
  ssl_client = nil
139
138
  expect {
140
139
  # rubocop:disable Style/TrailingComma
141
- ssl_client = Mysql2::Client.new(
142
- DatabaseCredentials['root'].merge(
143
- 'host' => 'mysql2gem.example.com', # must match the certificates
144
- :sslkey => '/etc/mysql/client-key.pem',
145
- :sslcert => '/etc/mysql/client-cert.pem',
146
- :sslca => '/etc/mysql/ca-cert.pem',
147
- :sslcipher => 'DHE-RSA-AES256-SHA',
148
- :sslverify => true
149
- )
140
+ ssl_client = new_client(
141
+ 'host' => 'mysql2gem.example.com', # must match the certificates
142
+ :sslkey => '/etc/mysql/client-key.pem',
143
+ :sslcert => '/etc/mysql/client-cert.pem',
144
+ :sslca => '/etc/mysql/ca-cert.pem',
145
+ :sslcipher => 'DHE-RSA-AES256-SHA',
146
+ :sslverify => true
150
147
  )
151
148
  # rubocop:enable Style/TrailingComma
152
149
  }.not_to raise_error
@@ -157,8 +154,6 @@ RSpec.describe Mysql2::Client do
157
154
 
158
155
  expect(ssl_client.ssl_cipher).not_to be_empty
159
156
  expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
160
-
161
- ssl_client.close
162
157
  end
163
158
 
164
159
  def run_gc
@@ -172,10 +167,21 @@ RSpec.describe Mysql2::Client do
172
167
 
173
168
  it "should terminate connections when calling close" do
174
169
  expect {
175
- Mysql2::Client.new(DatabaseCredentials['root']).close
170
+ client = Mysql2::Client.new(DatabaseCredentials['root'])
171
+ connection_id = client.thread_id
172
+ client.close
173
+
174
+ # mysql_close sends a quit command without waiting for a response
175
+ # so give the server some time to handle the detect the closed connection
176
+ closed = false
177
+ 10.times do
178
+ closed = @client.query("SHOW PROCESSLIST").none? { |row| row['Id'] == connection_id }
179
+ break if closed
180
+ sleep(0.1)
181
+ end
182
+ expect(closed).to eq(true)
176
183
  }.to_not change {
177
- @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
178
- @client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
184
+ @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a
179
185
  }
180
186
  end
181
187
 
@@ -199,36 +205,35 @@ RSpec.describe Mysql2::Client do
199
205
 
200
206
  context "#automatic_close" do
201
207
  it "is enabled by default" do
202
- client = Mysql2::Client.new(DatabaseCredentials['root'])
203
- expect(client.automatic_close?).to be(true)
208
+ expect(new_client.automatic_close?).to be(true)
204
209
  end
205
210
 
206
211
  if RUBY_PLATFORM =~ /mingw|mswin/
207
212
  it "cannot be disabled" do
208
213
  expect do
209
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
214
+ client = new_client(:automatic_close => false)
210
215
  expect(client.automatic_close?).to be(true)
211
216
  end.to output(/always closed by garbage collector/).to_stderr
212
217
 
213
218
  expect do
214
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
219
+ client = new_client(:automatic_close => true)
215
220
  expect(client.automatic_close?).to be(true)
216
221
  end.to_not output(/always closed by garbage collector/).to_stderr
217
222
 
218
223
  expect do
219
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
224
+ client = new_client(:automatic_close => true)
220
225
  client.automatic_close = false
221
226
  expect(client.automatic_close?).to be(true)
222
227
  end.to output(/always closed by garbage collector/).to_stderr
223
228
  end
224
229
  else
225
230
  it "can be configured" do
226
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
231
+ client = new_client(:automatic_close => false)
227
232
  expect(client.automatic_close?).to be(false)
228
233
  end
229
234
 
230
235
  it "can be assigned" do
231
- client = Mysql2::Client.new(DatabaseCredentials['root'])
236
+ client = new_client
232
237
  client.automatic_close = false
233
238
  expect(client.automatic_close?).to be(false)
234
239
 
@@ -247,21 +252,18 @@ RSpec.describe Mysql2::Client do
247
252
  client = Mysql2::Client.new(DatabaseCredentials['root'])
248
253
  client.automatic_close = false
249
254
 
250
- # this empty `fork` call fixes this tests on RBX; without it, the next
251
- # `fork` call hangs forever. WTF?
252
- fork {}
253
-
254
- fork do
255
+ child = fork do
255
256
  client.query('SELECT 1')
256
257
  client = nil
257
258
  run_gc
258
259
  end
259
260
 
260
- Process.wait
261
+ Process.wait(child)
261
262
 
262
263
  # this will throw an error if the underlying socket was shutdown by the
263
264
  # child's GC
264
265
  expect { client.query('SELECT 1') }.to_not raise_exception
266
+ client.close
265
267
  end
266
268
  end
267
269
  end
@@ -271,7 +273,7 @@ RSpec.describe Mysql2::Client do
271
273
  @client.query "CREATE DATABASE IF NOT EXISTS `#{database}`"
272
274
 
273
275
  expect {
274
- Mysql2::Client.new(DatabaseCredentials['root'].merge('database' => database))
276
+ new_client('database' => database)
275
277
  }.not_to raise_error
276
278
 
277
279
  @client.query "DROP DATABASE IF EXISTS `#{database}`"
@@ -288,6 +290,25 @@ RSpec.describe Mysql2::Client do
288
290
  }.to raise_error(Mysql2::Error)
289
291
  end
290
292
 
293
+ context "#closed?" do
294
+ it "should return false when connected" do
295
+ expect(@client.closed?).to eql(false)
296
+ end
297
+
298
+ it "should return true after close" do
299
+ @client.close
300
+ expect(@client.closed?).to eql(true)
301
+ end
302
+ end
303
+
304
+ it "should not try to query closed mysql connection" do
305
+ client = new_client(:reconnect => true)
306
+ expect(client.close).to be_nil
307
+ expect {
308
+ client.query "SELECT 1"
309
+ }.to raise_error(Mysql2::Error)
310
+ end
311
+
291
312
  it "should respond to #query" do
292
313
  expect(@client).to respond_to(:query)
293
314
  end
@@ -344,67 +365,72 @@ RSpec.describe Mysql2::Client do
344
365
 
345
366
  context ":local_infile" do
346
367
  before(:all) do
347
- @client_i = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => true)
348
- local = @client_i.query "SHOW VARIABLES LIKE 'local_infile'"
349
- local_enabled = local.any? { |x| x['Value'] == 'ON' }
350
- pending("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
351
-
352
- @client_i.query %[
353
- CREATE TABLE IF NOT EXISTS infileTest (
354
- id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
355
- foo VARCHAR(10),
356
- bar MEDIUMTEXT
357
- )
358
- ]
368
+ new_client(:local_infile => true) do |client|
369
+ local = client.query "SHOW VARIABLES LIKE 'local_infile'"
370
+ local_enabled = local.any? { |x| x['Value'] == 'ON' }
371
+ pending("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
372
+
373
+ client.query %[
374
+ CREATE TABLE IF NOT EXISTS infileTest (
375
+ id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
376
+ foo VARCHAR(10),
377
+ bar MEDIUMTEXT
378
+ )
379
+ ]
380
+ end
359
381
  end
360
382
 
361
383
  after(:all) do
362
- @client_i.query "DROP TABLE infileTest"
384
+ new_client do |client|
385
+ client.query "DROP TABLE infileTest"
386
+ end
363
387
  end
364
388
 
365
389
  it "should raise an error when local_infile is disabled" do
366
- client = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => false)
390
+ client = new_client(:local_infile => false)
367
391
  expect {
368
392
  client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
369
393
  }.to raise_error(Mysql2::Error, /command is not allowed/)
370
394
  end
371
395
 
372
396
  it "should raise an error when a non-existent file is loaded" do
397
+ client = new_client(:local_infile => true)
373
398
  expect {
374
- @client_i.query "LOAD DATA LOCAL INFILE 'this/file/is/not/here' INTO TABLE infileTest"
399
+ client.query "LOAD DATA LOCAL INFILE 'this/file/is/not/here' INTO TABLE infileTest"
375
400
  }.to raise_error(Mysql2::Error, 'No such file or directory: this/file/is/not/here')
376
401
  end
377
402
 
378
403
  it "should LOAD DATA LOCAL INFILE" do
379
- @client_i.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
380
- info = @client_i.query_info
404
+ client = new_client(:local_infile => true)
405
+ client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
406
+ info = client.query_info
381
407
  expect(info).to eql(:records => 1, :deleted => 0, :skipped => 0, :warnings => 0)
382
408
 
383
- result = @client_i.query "SELECT * FROM infileTest"
409
+ result = client.query "SELECT * FROM infileTest"
384
410
  expect(result.first).to eql('id' => 1, 'foo' => 'Hello', 'bar' => 'World')
385
411
  end
386
412
  end
387
413
 
388
414
  it "should expect connect_timeout to be a positive integer" do
389
415
  expect {
390
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:connect_timeout => -1))
416
+ new_client(:connect_timeout => -1)
391
417
  }.to raise_error(Mysql2::Error)
392
418
  end
393
419
 
394
420
  it "should expect read_timeout to be a positive integer" do
395
421
  expect {
396
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => -1))
422
+ new_client(:read_timeout => -1)
397
423
  }.to raise_error(Mysql2::Error)
398
424
  end
399
425
 
400
426
  it "should expect write_timeout to be a positive integer" do
401
427
  expect {
402
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:write_timeout => -1))
428
+ new_client(:write_timeout => -1)
403
429
  }.to raise_error(Mysql2::Error)
404
430
  end
405
431
 
406
432
  it "should allow nil read_timeout" do
407
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => nil))
433
+ client = new_client(:read_timeout => nil)
408
434
 
409
435
  expect(client.read_timeout).to be_nil
410
436
  end
@@ -474,6 +500,25 @@ RSpec.describe Mysql2::Client do
474
500
  }.to raise_error(Mysql2::Error)
475
501
  end
476
502
 
503
+ it "should detect closed connection on query read error" do
504
+ connection_id = @client.thread_id
505
+ Thread.new do
506
+ sleep(0.1)
507
+ Mysql2::Client.new(DatabaseCredentials['root']).tap do |supervisor|
508
+ supervisor.query("KILL #{connection_id}")
509
+ end.close
510
+ end
511
+ expect {
512
+ @client.query("SELECT SLEEP(1)")
513
+ }.to raise_error(Mysql2::Error, /Lost connection to MySQL server/)
514
+
515
+ if RUBY_PLATFORM !~ /mingw|mswin/
516
+ expect {
517
+ @client.socket
518
+ }.to raise_error(Mysql2::Error, 'MySQL client is not connected')
519
+ end
520
+ end
521
+
477
522
  if RUBY_PLATFORM !~ /mingw|mswin/
478
523
  it "should not allow another query to be sent without fetching a result first" do
479
524
  @client.query("SELECT 1", :async => true)
@@ -490,7 +535,7 @@ RSpec.describe Mysql2::Client do
490
535
  end
491
536
 
492
537
  it "should timeout if we wait longer than :read_timeout" do
493
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => 0))
538
+ client = new_client(:read_timeout => 0)
494
539
  expect {
495
540
  client.query('SELECT SLEEP(0.1)')
496
541
  }.to raise_error(Mysql2::Error)
@@ -563,7 +608,7 @@ RSpec.describe Mysql2::Client do
563
608
  pending('libmysqlclient 5.5 on OSX is afflicted by an unknown bug that breaks this test. See #633 and #634.')
564
609
  end
565
610
 
566
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
611
+ client = new_client(:reconnect => true)
567
612
 
568
613
  expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
569
614
  expect { client.query('SELECT 1') }.to_not raise_error
@@ -574,7 +619,7 @@ RSpec.describe Mysql2::Client do
574
619
  pending('libmysqlclient 5.5 on OSX is afflicted by an unknown bug that breaks this test. See #633 and #634.')
575
620
  end
576
621
 
577
- client = Mysql2::Client.new(DatabaseCredentials['root'])
622
+ client = new_client
578
623
 
579
624
  expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
580
625
  expect { client.query('SELECT 1') }.to raise_error(Mysql2::Error)
@@ -592,8 +637,9 @@ RSpec.describe Mysql2::Client do
592
637
  # Note that each thread opens its own database connection
593
638
  threads = 5.times.map do
594
639
  Thread.new do
595
- client = Mysql2::Client.new(DatabaseCredentials.fetch('root'))
596
- client.query("SELECT SLEEP(#{sleep_time})")
640
+ new_client do |client|
641
+ client.query("SELECT SLEEP(#{sleep_time})")
642
+ end
597
643
  Thread.current.object_id
598
644
  end
599
645
  end
@@ -606,10 +652,11 @@ RSpec.describe Mysql2::Client do
606
652
  end
607
653
 
608
654
  it "evented async queries should be supported" do
655
+ skip("ruby 1.8 doesn't support IO.for_fd options") if RUBY_VERSION.start_with?("1.8.")
609
656
  # should immediately return nil
610
657
  expect(@client.query("SELECT sleep(0.1)", :async => true)).to eql(nil)
611
658
 
612
- io_wrapper = IO.for_fd(@client.socket)
659
+ io_wrapper = IO.for_fd(@client.socket, :autoclose => false)
613
660
  loops = 0
614
661
  loop do
615
662
  if IO.select([io_wrapper], nil, nil, 0.05)
@@ -629,7 +676,7 @@ RSpec.describe Mysql2::Client do
629
676
 
630
677
  context "Multiple results sets" do
631
678
  before(:each) do
632
- @multi_client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:flags => Mysql2::Client::MULTI_STATEMENTS))
679
+ @multi_client = new_client(:flags => Mysql2::Client::MULTI_STATEMENTS)
633
680
  end
634
681
 
635
682
  it "should raise an exception when one of multiple statements fails" do
@@ -707,7 +754,7 @@ RSpec.describe Mysql2::Client do
707
754
  it "#socket should raise as it's not supported" do
708
755
  expect {
709
756
  @client.socket
710
- }.to raise_error(Mysql2::Error)
757
+ }.to raise_error(Mysql2::Error, /Raw access to the mysql file descriptor isn't supported on Windows/)
711
758
  end
712
759
  end
713
760
 
@@ -786,7 +833,7 @@ RSpec.describe Mysql2::Client do
786
833
  context 'when mysql encoding is not utf8' do
787
834
  before { pending('Encoding is undefined') unless defined?(Encoding) }
788
835
 
789
- let(:client) { Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "ujis")) }
836
+ let(:client) { new_client(:encoding => "ujis") }
790
837
 
791
838
  it 'should return a internal encoding string if Encoding.default_internal is set' do
792
839
  with_internal_encoding Encoding::UTF_8 do
@@ -855,7 +902,7 @@ RSpec.describe Mysql2::Client do
855
902
  with_internal_encoding nil do
856
903
  expect(@client.server_info[:version].encoding).to eql(Encoding::UTF_8)
857
904
 
858
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
905
+ client2 = new_client(:encoding => 'ascii')
859
906
  expect(client2.server_info[:version].encoding).to eql(Encoding::ASCII)
860
907
  end
861
908
  end
@@ -873,11 +920,11 @@ RSpec.describe Mysql2::Client do
873
920
 
874
921
  it "should raise a Mysql2::Error exception upon connection failure" do
875
922
  expect {
876
- Mysql2::Client.new :host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42'
923
+ new_client(:host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42')
877
924
  }.to raise_error(Mysql2::Error)
878
925
 
879
926
  expect {
880
- Mysql2::Client.new DatabaseCredentials['root']
927
+ new_client(DatabaseCredentials['root'])
881
928
  }.not_to raise_error
882
929
  end
883
930
 
@@ -3,11 +3,9 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Mysql2::Error do
6
- let(:client) { Mysql2::Client.new(DatabaseCredentials['root']) }
7
-
8
6
  let(:error) do
9
7
  begin
10
- client.query("HAHAHA")
8
+ @client.query("HAHAHA")
11
9
  rescue Mysql2::Error => e
12
10
  error = e
13
11
  end
@@ -28,7 +26,7 @@ RSpec.describe Mysql2::Error do
28
26
  let(:valid_utf8) { '造字' }
29
27
  let(:error) do
30
28
  begin
31
- client.query(valid_utf8)
29
+ @client.query(valid_utf8)
32
30
  rescue Mysql2::Error => e
33
31
  e
34
32
  end
@@ -37,7 +35,7 @@ RSpec.describe Mysql2::Error do
37
35
  let(:invalid_utf8) { ["e5c67d1f"].pack('H*').force_encoding(Encoding::UTF_8) }
38
36
  let(:bad_err) do
39
37
  begin
40
- client.query(invalid_utf8)
38
+ @client.query(invalid_utf8)
41
39
  rescue Mysql2::Error => e
42
40
  e
43
41
  end
@@ -153,18 +153,16 @@ RSpec.describe Mysql2::Result do
153
153
  end
154
154
 
155
155
  it "should raise an exception if streaming ended due to a timeout" do
156
- # Create an extra client instance, since we're going to time it out
157
- client = Mysql2::Client.new DatabaseCredentials['root']
158
- client.query "CREATE TEMPORARY TABLE streamingTest (val BINARY(255)) ENGINE=MEMORY"
156
+ @client.query "CREATE TEMPORARY TABLE streamingTest (val BINARY(255)) ENGINE=MEMORY"
159
157
 
160
158
  # Insert enough records to force the result set into multiple reads
161
159
  # (the BINARY type is used simply because it forces full width results)
162
160
  10000.times do |i|
163
- client.query "INSERT INTO streamingTest (val) VALUES ('Foo #{i}')"
161
+ @client.query "INSERT INTO streamingTest (val) VALUES ('Foo #{i}')"
164
162
  end
165
163
 
166
- client.query "SET net_write_timeout = 1"
167
- res = client.query "SELECT * FROM streamingTest", :stream => true, :cache_rows => false
164
+ @client.query "SET net_write_timeout = 1"
165
+ res = @client.query "SELECT * FROM streamingTest", :stream => true, :cache_rows => false
168
166
 
169
167
  expect {
170
168
  res.each_with_index do |_, i|
@@ -367,10 +365,9 @@ RSpec.describe Mysql2::Result do
367
365
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
368
366
  expect(result['enum_test'].encoding).to eql(Encoding::UTF_8)
369
367
 
370
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
368
+ client2 = new_client(:encoding => 'ascii')
371
369
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
372
370
  expect(result['enum_test'].encoding).to eql(Encoding::ASCII)
373
- client2.close
374
371
  end
375
372
  end
376
373
 
@@ -400,10 +397,9 @@ RSpec.describe Mysql2::Result do
400
397
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
401
398
  expect(result['set_test'].encoding).to eql(Encoding::UTF_8)
402
399
 
403
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
400
+ client2 = new_client(:encoding => 'ascii')
404
401
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
405
402
  expect(result['set_test'].encoding).to eql(Encoding::ASCII)
406
- client2.close
407
403
  end
408
404
  end
409
405
 
@@ -494,10 +490,9 @@ RSpec.describe Mysql2::Result do
494
490
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
495
491
  expect(result[field].encoding).to eql(Encoding::UTF_8)
496
492
 
497
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
493
+ client2 = new_client(:encoding => 'ascii')
498
494
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
499
495
  expect(result[field].encoding).to eql(Encoding::ASCII)
500
- client2.close
501
496
  end
502
497
  end
503
498
 
@@ -3,7 +3,7 @@ require './spec/spec_helper.rb'
3
3
 
4
4
  RSpec.describe Mysql2::Statement do
5
5
  before :each do
6
- @client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
6
+ @client = new_client(:encoding => "utf8")
7
7
  end
8
8
 
9
9
  def stmt_count
@@ -326,18 +326,19 @@ RSpec.describe Mysql2::Statement do
326
326
  end
327
327
 
328
328
  context "#fields" do
329
- before(:each) do
330
- @client.query "USE test"
331
- @test_result = @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute
332
- end
333
-
334
329
  it "method should exist" do
335
- expect(@test_result).to respond_to(:fields)
330
+ stmt = @client.prepare("SELECT 1")
331
+ expect(stmt).to respond_to(:fields)
336
332
  end
337
333
 
338
334
  it "should return an array of field names in proper order" do
339
- result = @client.prepare("SELECT 'a', 'b', 'c'").execute
340
- expect(result.fields).to eql(%w(a b c))
335
+ stmt = @client.prepare("SELECT 'a', 'b', 'c'")
336
+ expect(stmt.fields).to eql(%w(a b c))
337
+ end
338
+
339
+ it "should return nil for statement with no result fields" do
340
+ stmt = @client.prepare("INSERT INTO mysql2_test () VALUES ()")
341
+ expect(stmt.fields).to eql(nil)
341
342
  end
342
343
  end
343
344
 
@@ -524,10 +525,9 @@ RSpec.describe Mysql2::Statement do
524
525
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
525
526
  expect(result['enum_test'].encoding).to eql(Encoding::UTF_8)
526
527
 
527
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
528
+ client2 = new_client(:encoding => 'ascii')
528
529
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
529
530
  expect(result['enum_test'].encoding).to eql(Encoding::US_ASCII)
530
- client2.close
531
531
  end
532
532
  end
533
533
 
@@ -557,10 +557,9 @@ RSpec.describe Mysql2::Statement do
557
557
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
558
558
  expect(result['set_test'].encoding).to eql(Encoding::UTF_8)
559
559
 
560
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
560
+ client2 = new_client(:encoding => 'ascii')
561
561
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
562
562
  expect(result['set_test'].encoding).to eql(Encoding::US_ASCII)
563
- client2.close
564
563
  end
565
564
  end
566
565
 
@@ -651,10 +650,9 @@ RSpec.describe Mysql2::Statement do
651
650
  result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
652
651
  expect(result[field].encoding).to eql(Encoding::UTF_8)
653
652
 
654
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
653
+ client2 = new_client(:encoding => 'ascii')
655
654
  result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
656
655
  expect(result[field].encoding).to eql(Encoding::US_ASCII)
657
- client2.close
658
656
  end
659
657
  end
660
658
 
@@ -23,72 +23,86 @@ RSpec.configure do |config|
23
23
  $VERBOSE = old_verbose
24
24
  end
25
25
 
26
+ def new_client(option_overrides = {})
27
+ client = Mysql2::Client.new(DatabaseCredentials['root'].merge(option_overrides))
28
+ @clients ||= []
29
+ @clients << client
30
+ return client unless block_given?
31
+ begin
32
+ yield client
33
+ ensure
34
+ client.close
35
+ @clients.delete(client)
36
+ end
37
+ end
38
+
26
39
  config.before :each do
27
- @client = Mysql2::Client.new DatabaseCredentials['root']
40
+ @client = new_client
28
41
  end
29
42
 
30
43
  config.after :each do
31
- @client.close
44
+ @clients.each(&:close)
32
45
  end
33
46
 
34
47
  config.before(:all) do
35
- client = Mysql2::Client.new DatabaseCredentials['root']
36
- client.query %[
37
- CREATE TABLE IF NOT EXISTS mysql2_test (
38
- id MEDIUMINT NOT NULL AUTO_INCREMENT,
39
- null_test VARCHAR(10),
40
- bit_test BIT(64),
41
- single_bit_test BIT(1),
42
- tiny_int_test TINYINT,
43
- bool_cast_test TINYINT(1),
44
- small_int_test SMALLINT,
45
- medium_int_test MEDIUMINT,
46
- int_test INT,
47
- big_int_test BIGINT,
48
- float_test FLOAT(10,3),
49
- float_zero_test FLOAT(10,3),
50
- double_test DOUBLE(10,3),
51
- decimal_test DECIMAL(10,3),
52
- decimal_zero_test DECIMAL(10,3),
53
- date_test DATE,
54
- date_time_test DATETIME,
55
- timestamp_test TIMESTAMP,
56
- time_test TIME,
57
- year_test YEAR(4),
58
- char_test CHAR(10),
59
- varchar_test VARCHAR(10),
60
- binary_test BINARY(10),
61
- varbinary_test VARBINARY(10),
62
- tiny_blob_test TINYBLOB,
63
- tiny_text_test TINYTEXT,
64
- blob_test BLOB,
65
- text_test TEXT,
66
- medium_blob_test MEDIUMBLOB,
67
- medium_text_test MEDIUMTEXT,
68
- long_blob_test LONGBLOB,
69
- long_text_test LONGTEXT,
70
- enum_test ENUM('val1', 'val2'),
71
- set_test SET('val1', 'val2'),
72
- PRIMARY KEY (id)
73
- )
74
- ]
75
- client.query "DELETE FROM mysql2_test;"
76
- client.query %[
77
- INSERT INTO mysql2_test (
78
- null_test, bit_test, single_bit_test, tiny_int_test, bool_cast_test, small_int_test, medium_int_test, int_test, big_int_test,
79
- float_test, float_zero_test, double_test, decimal_test, decimal_zero_test, date_test, date_time_test, timestamp_test, time_test,
80
- year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
81
- tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
82
- long_blob_test, long_text_test, enum_test, set_test
83
- )
48
+ new_client do |client|
49
+ client.query %[
50
+ CREATE TABLE IF NOT EXISTS mysql2_test (
51
+ id MEDIUMINT NOT NULL AUTO_INCREMENT,
52
+ null_test VARCHAR(10),
53
+ bit_test BIT(64),
54
+ single_bit_test BIT(1),
55
+ tiny_int_test TINYINT,
56
+ bool_cast_test TINYINT(1),
57
+ small_int_test SMALLINT,
58
+ medium_int_test MEDIUMINT,
59
+ int_test INT,
60
+ big_int_test BIGINT,
61
+ float_test FLOAT(10,3),
62
+ float_zero_test FLOAT(10,3),
63
+ double_test DOUBLE(10,3),
64
+ decimal_test DECIMAL(10,3),
65
+ decimal_zero_test DECIMAL(10,3),
66
+ date_test DATE,
67
+ date_time_test DATETIME,
68
+ timestamp_test TIMESTAMP,
69
+ time_test TIME,
70
+ year_test YEAR(4),
71
+ char_test CHAR(10),
72
+ varchar_test VARCHAR(10),
73
+ binary_test BINARY(10),
74
+ varbinary_test VARBINARY(10),
75
+ tiny_blob_test TINYBLOB,
76
+ tiny_text_test TINYTEXT,
77
+ blob_test BLOB,
78
+ text_test TEXT,
79
+ medium_blob_test MEDIUMBLOB,
80
+ medium_text_test MEDIUMTEXT,
81
+ long_blob_test LONGBLOB,
82
+ long_text_test LONGTEXT,
83
+ enum_test ENUM('val1', 'val2'),
84
+ set_test SET('val1', 'val2'),
85
+ PRIMARY KEY (id)
86
+ )
87
+ ]
88
+ client.query "DELETE FROM mysql2_test;"
89
+ client.query %[
90
+ INSERT INTO mysql2_test (
91
+ null_test, bit_test, single_bit_test, tiny_int_test, bool_cast_test, small_int_test, medium_int_test, int_test, big_int_test,
92
+ float_test, float_zero_test, double_test, decimal_test, decimal_zero_test, date_test, date_time_test, timestamp_test, time_test,
93
+ year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
94
+ tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
95
+ long_blob_test, long_text_test, enum_test, set_test
96
+ )
84
97
 
85
- VALUES (
86
- NULL, b'101', b'1', 1, 1, 10, 10, 10, 10,
87
- 10.3, 0, 10.3, 10.3, 0, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00',
88
- 2009, "test", "test", "test", "test", "test",
89
- "test", "test", "test", "test", "test",
90
- "test", "test", 'val1', 'val1,val2'
91
- )
92
- ]
98
+ VALUES (
99
+ NULL, b'101', b'1', 1, 1, 10, 10, 10, 10,
100
+ 10.3, 0, 10.3, 10.3, 0, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00',
101
+ 2009, "test", "test", "test", "test", "test",
102
+ "test", "test", "test", "test", "test",
103
+ "test", "test", 'val1', 'val1,val2'
104
+ )
105
+ ]
106
+ end
93
107
  end
94
108
  end
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.6
4
+ version: 0.4.7
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: 2017-05-04 00:00:00.000000000 Z
12
+ date: 2017-07-01 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: