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 +4 -4
- data/README.md +57 -42
- data/ext/mysql2/client.c +55 -23
- data/ext/mysql2/client.h +1 -2
- data/ext/mysql2/extconf.rb +5 -2
- data/ext/mysql2/mysql2_ext.h +2 -0
- data/ext/mysql2/statement.c +13 -2
- data/lib/mysql2/client.rb +3 -3
- data/lib/mysql2/version.rb +1 -1
- data/spec/em/em_spec.rb +1 -0
- data/spec/mysql2/client_spec.rb +119 -72
- data/spec/mysql2/error_spec.rb +3 -5
- data/spec/mysql2/result_spec.rb +7 -12
- data/spec/mysql2/statement_spec.rb +13 -15
- data/spec/spec_helper.rb +73 -59
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49904d19056ae4c90f12a539d56dfc3789fd8bc8
|
4
|
+
data.tar.gz: a9c541dd2693bc2bce4eba0a81654e0cefd2d704
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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:
|
data/ext/mysql2/client.c
CHANGED
@@ -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
|
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
|
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->
|
272
|
+
if (!wrapper->closed) {
|
268
273
|
mysql_close(wrapper->client);
|
269
|
-
|
270
|
-
wrapper->
|
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
|
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->
|
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
|
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
|
-
|
674
|
+
if (CONNECTED(wrapper)) {
|
675
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
660
676
|
#ifndef _WIN32
|
661
|
-
|
662
|
-
|
663
|
-
|
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
|
-
|
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
|
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
|
data/ext/mysql2/client.h
CHANGED
data/ext/mysql2/extconf.rb
CHANGED
@@ -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 = [
|
data/ext/mysql2/mysql2_ext.h
CHANGED
@@ -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
|
data/ext/mysql2/statement.c
CHANGED
@@ -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
|
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);
|
data/lib/mysql2/client.rb
CHANGED
@@ -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
|
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
|
data/lib/mysql2/version.rb
CHANGED
data/spec/em/em_spec.rb
CHANGED
@@ -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 = []
|
data/spec/mysql2/client_spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
43
|
+
new_client(:encoding => "utf8")
|
45
44
|
}.not_to raise_error
|
46
45
|
|
47
46
|
expect {
|
48
|
-
|
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 =
|
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 =
|
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 =
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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'])
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
|
380
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
596
|
-
|
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 =
|
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) {
|
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 =
|
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
|
-
|
923
|
+
new_client(:host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42')
|
877
924
|
}.to raise_error(Mysql2::Error)
|
878
925
|
|
879
926
|
expect {
|
880
|
-
|
927
|
+
new_client(DatabaseCredentials['root'])
|
881
928
|
}.not_to raise_error
|
882
929
|
end
|
883
930
|
|
data/spec/mysql2/error_spec.rb
CHANGED
@@ -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
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -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
|
-
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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
|
-
|
340
|
-
expect(
|
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 =
|
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 =
|
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 =
|
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
|
|
data/spec/spec_helper.rb
CHANGED
@@ -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 =
|
40
|
+
@client = new_client
|
28
41
|
end
|
29
42
|
|
30
43
|
config.after :each do
|
31
|
-
@
|
44
|
+
@clients.each(&:close)
|
32
45
|
end
|
33
46
|
|
34
47
|
config.before(:all) do
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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.
|
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-
|
12
|
+
date: 2017-07-01 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email:
|