mysql2 0.4.4 → 0.4.10
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 +72 -47
- data/ext/mysql2/client.c +165 -30
- data/ext/mysql2/client.h +1 -8
- data/ext/mysql2/extconf.rb +26 -2
- data/ext/mysql2/mysql2_ext.h +0 -4
- data/ext/mysql2/result.c +12 -3
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +106 -15
- data/lib/mysql2/client.rb +19 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +1 -0
- data/spec/mysql2/client_spec.rb +152 -104
- data/spec/mysql2/error_spec.rb +4 -6
- data/spec/mysql2/result_spec.rb +62 -36
- data/spec/mysql2/statement_spec.rb +125 -52
- data/spec/spec_helper.rb +73 -59
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- metadata +12 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75b3d925930b92cf7b1a36fa196d334e245919ac
|
4
|
+
data.tar.gz: 2bbe0a78b156f8c5b59643c4d57a7ce19b764bcc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 602f336b5ed83421862b9dec36a9ddbd477dcbacc3ef16d58d5252072dba0bc7f7a955000482414eda1d104bda72ded87f3f4c795f8b4b4d36999bc6ee171e4b
|
7
|
+
data.tar.gz: 20281fda66cf4595edc05ac6a933d5f641c2f9f87771e8ace1e9de00902ecea54ddbc2d1b743c3dbd97b48c795d0ad32f9ab785e5848a4f3de92c7ddebeef659
|
data/README.md
CHANGED
@@ -85,6 +85,9 @@ You may use MacPorts, Homebrew, or a native MySQL installer package. The most
|
|
85
85
|
common paths will be automatically searched. If you want to select a specific
|
86
86
|
MySQL directory, use the `--with-mysql-dir` or `--with-mysql-config` options above.
|
87
87
|
|
88
|
+
If you have not done so already, you will need to install the XCode select tools by running
|
89
|
+
`xcode-select --install`.
|
90
|
+
|
88
91
|
### Windows
|
89
92
|
Make sure that you have Ruby and the DevKit compilers installed. We recommend
|
90
93
|
the [Ruby Installer](http://rubyinstaller.org) distribution.
|
@@ -109,7 +112,7 @@ Connect to a database:
|
|
109
112
|
``` ruby
|
110
113
|
# this takes a hash of options, almost all of which map directly
|
111
114
|
# to the familiar database.yml in rails
|
112
|
-
# See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/
|
115
|
+
# See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html
|
113
116
|
client = Mysql2::Client.new(:host => "localhost", :username => "root")
|
114
117
|
```
|
115
118
|
|
@@ -164,8 +167,8 @@ by the query like this:
|
|
164
167
|
``` ruby
|
165
168
|
headers = results.fields # <= that's an array of field names, in order
|
166
169
|
results.each(:as => :array) do |row|
|
167
|
-
# Each row is an array, ordered the same as the query results
|
168
|
-
# An otter's den is called a "holt" or "couch"
|
170
|
+
# Each row is an array, ordered the same as the query results
|
171
|
+
# An otter's den is called a "holt" or "couch"
|
169
172
|
end
|
170
173
|
```
|
171
174
|
|
@@ -203,12 +206,25 @@ Mysql2::Client.new(
|
|
203
206
|
:reconnect = true/false,
|
204
207
|
:local_infile = true/false,
|
205
208
|
:secure_auth = true/false,
|
209
|
+
:ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
|
206
210
|
:default_file = '/path/to/my.cfg',
|
207
211
|
:default_group = 'my.cfg section',
|
208
212
|
:init_command => sql
|
209
213
|
)
|
210
214
|
```
|
211
215
|
|
216
|
+
### Connecting to MySQL on localhost and elsewhere
|
217
|
+
|
218
|
+
The underlying MySQL client library uses the `:host` parameter to determine the
|
219
|
+
type of connection to make, with special interpretation you should be aware of:
|
220
|
+
|
221
|
+
* An empty value or `"localhost"` will attempt a local connection:
|
222
|
+
* On Unix, connect to the default local socket path. (To set a custom socket
|
223
|
+
path, use the `:socket` parameter).
|
224
|
+
* On Windows, connect using a shared-memory connection, if enabled, or TCP.
|
225
|
+
* A value of `"."` on Windows specifies a named-pipe connection.
|
226
|
+
* An IPv4 or IPv6 address will result in a TCP connection.
|
227
|
+
* Any other value will be looked up as a hostname for a TCP connection.
|
212
228
|
|
213
229
|
### SSL options
|
214
230
|
|
@@ -231,47 +247,6 @@ Mysql2::Client.new(
|
|
231
247
|
)
|
232
248
|
```
|
233
249
|
|
234
|
-
### Multiple result sets
|
235
|
-
|
236
|
-
You can also retrieve multiple result sets. For this to work you need to
|
237
|
-
connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
|
238
|
-
be used with stored procedures that return more than one result set, and for
|
239
|
-
bundling several SQL statements into a single call to `client.query`.
|
240
|
-
|
241
|
-
``` ruby
|
242
|
-
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
|
243
|
-
result = client.query('CALL sp_customer_list( 25, 10 )')
|
244
|
-
# result now contains the first result set
|
245
|
-
while client.next_result
|
246
|
-
result = client.store_result
|
247
|
-
# result now contains the next result set
|
248
|
-
end
|
249
|
-
```
|
250
|
-
|
251
|
-
Repeated calls to `client.next_result` will return true, false, or raise an
|
252
|
-
exception if the respective query erred. When `client.next_result` returns true,
|
253
|
-
call `client.store_result` to retrieve a result object. Exceptions are not
|
254
|
-
raised until `client.next_result` is called to find the status of the respective
|
255
|
-
query. Subsequent queries are not executed if an earlier query raised an
|
256
|
-
exception. Subsequent calls to `client.next_result` will return false.
|
257
|
-
|
258
|
-
``` ruby
|
259
|
-
result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
|
260
|
-
p result.first
|
261
|
-
|
262
|
-
while client.next_result
|
263
|
-
result = client.store_result
|
264
|
-
p result.first
|
265
|
-
end
|
266
|
-
```
|
267
|
-
|
268
|
-
Yields:
|
269
|
-
```
|
270
|
-
{"1"=>1}
|
271
|
-
{"2"=>2}
|
272
|
-
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
273
|
-
```
|
274
|
-
|
275
250
|
### Secure auth
|
276
251
|
|
277
252
|
Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
|
@@ -328,6 +303,47 @@ It is useful if you want to provide session options which survive reconnection.
|
|
328
303
|
Mysql2::Client.new(:init_command => "SET @@SESSION.sql_mode = 'STRICT_ALL_TABLES'")
|
329
304
|
```
|
330
305
|
|
306
|
+
### Multiple result sets
|
307
|
+
|
308
|
+
You can also retrieve multiple result sets. For this to work you need to
|
309
|
+
connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
|
310
|
+
be used with stored procedures that return more than one result set, and for
|
311
|
+
bundling several SQL statements into a single call to `client.query`.
|
312
|
+
|
313
|
+
``` ruby
|
314
|
+
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
|
315
|
+
result = client.query('CALL sp_customer_list( 25, 10 )')
|
316
|
+
# result now contains the first result set
|
317
|
+
while client.next_result
|
318
|
+
result = client.store_result
|
319
|
+
# result now contains the next result set
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
323
|
+
Repeated calls to `client.next_result` will return true, false, or raise an
|
324
|
+
exception if the respective query erred. When `client.next_result` returns true,
|
325
|
+
call `client.store_result` to retrieve a result object. Exceptions are not
|
326
|
+
raised until `client.next_result` is called to find the status of the respective
|
327
|
+
query. Subsequent queries are not executed if an earlier query raised an
|
328
|
+
exception. Subsequent calls to `client.next_result` will return false.
|
329
|
+
|
330
|
+
``` ruby
|
331
|
+
result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
|
332
|
+
p result.first
|
333
|
+
|
334
|
+
while client.next_result
|
335
|
+
result = client.store_result
|
336
|
+
p result.first
|
337
|
+
end
|
338
|
+
```
|
339
|
+
|
340
|
+
Yields:
|
341
|
+
```
|
342
|
+
{"1"=>1}
|
343
|
+
{"2"=>2}
|
344
|
+
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
345
|
+
```
|
346
|
+
|
331
347
|
## Cascading config
|
332
348
|
|
333
349
|
The default config hash is at:
|
@@ -398,6 +414,15 @@ client = Mysql2::Client.new
|
|
398
414
|
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
399
415
|
```
|
400
416
|
|
417
|
+
Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
|
418
|
+
|
419
|
+
``` ruby
|
420
|
+
client = Mysql2::Client.new
|
421
|
+
result = client.query("SELECT true", :cast_booleans => true)
|
422
|
+
```
|
423
|
+
|
424
|
+
CAST function wouldn't help here as there's no way to cast to TINYINT(1). Apparently the only way to solve this is to use a stored procedure with return type set to TINYINT(1).
|
425
|
+
|
401
426
|
### Skipping casting
|
402
427
|
|
403
428
|
Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false. (Note that :cast => false overrides :cast_booleans => true.)
|
@@ -484,13 +509,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
484
509
|
|
485
510
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
486
511
|
|
487
|
-
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x
|
512
|
+
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
|
488
513
|
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
489
|
-
* Rubinius 2.x
|
514
|
+
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
490
515
|
|
491
516
|
This gem is tested with the following MySQL and MariaDB versions:
|
492
517
|
|
493
|
-
* MySQL 5.5, 5.6, 5.7
|
518
|
+
* MySQL 5.5, 5.6, 5.7, 8.0
|
494
519
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
495
520
|
* MariaDB 5.5, 10.0, 10.1
|
496
521
|
|
data/ext/mysql2/client.c
CHANGED
@@ -30,9 +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
|
+
|
39
|
+
#define REQUIRE_CONNECTED(wrapper) \
|
40
|
+
REQUIRE_INITIALIZED(wrapper) \
|
41
|
+
if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
|
42
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
43
|
+
}
|
44
|
+
|
33
45
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
34
46
|
REQUIRE_INITIALIZED(wrapper) \
|
35
|
-
if (wrapper
|
47
|
+
if (CONNECTED(wrapper)) { \
|
36
48
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
37
49
|
}
|
38
50
|
|
@@ -41,12 +53,24 @@ VALUE rb_hash_dup(VALUE other) {
|
|
41
53
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
42
54
|
* linking against the server itself
|
43
55
|
*/
|
44
|
-
#
|
56
|
+
#if defined(MARIADB_CLIENT_VERSION_STR)
|
57
|
+
#define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
|
58
|
+
#elif defined(LIBMYSQL_VERSION)
|
45
59
|
#define MYSQL_LINK_VERSION LIBMYSQL_VERSION
|
46
60
|
#else
|
47
61
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
48
62
|
#endif
|
49
63
|
|
64
|
+
/*
|
65
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
66
|
+
*/
|
67
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
68
|
+
#define SSL_MODE_DISABLED 1
|
69
|
+
#define SSL_MODE_REQUIRED 3
|
70
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
71
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
72
|
+
#endif
|
73
|
+
|
50
74
|
/*
|
51
75
|
* used to pass all arguments to mysql_real_connect while inside
|
52
76
|
* rb_thread_call_without_gvl
|
@@ -83,6 +107,42 @@ struct nogvl_select_db_args {
|
|
83
107
|
char *db;
|
84
108
|
};
|
85
109
|
|
110
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
111
|
+
unsigned long version = mysql_get_client_version();
|
112
|
+
|
113
|
+
if (version < 50703) {
|
114
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
115
|
+
return Qnil;
|
116
|
+
}
|
117
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
118
|
+
GET_CLIENT(self);
|
119
|
+
int val = NUM2INT( setting );
|
120
|
+
if (version >= 50703 && version < 50711) {
|
121
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
122
|
+
bool b = ( val == SSL_MODE_REQUIRED );
|
123
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
124
|
+
return INT2NUM(result);
|
125
|
+
} else {
|
126
|
+
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
127
|
+
return Qnil;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
#endif
|
131
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
132
|
+
GET_CLIENT(self);
|
133
|
+
int val = NUM2INT( setting );
|
134
|
+
|
135
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
136
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
137
|
+
}
|
138
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
139
|
+
|
140
|
+
return INT2NUM(result);
|
141
|
+
#endif
|
142
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
143
|
+
return Qnil;
|
144
|
+
#endif
|
145
|
+
}
|
86
146
|
/*
|
87
147
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
88
148
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -211,11 +271,10 @@ static VALUE invalidate_fd(int clientfd)
|
|
211
271
|
static void *nogvl_close(void *ptr) {
|
212
272
|
mysql_client_wrapper *wrapper = ptr;
|
213
273
|
|
214
|
-
if (wrapper->
|
274
|
+
if (!wrapper->closed) {
|
215
275
|
mysql_close(wrapper->client);
|
216
|
-
|
217
|
-
wrapper->
|
218
|
-
wrapper->connected = 0;
|
276
|
+
wrapper->closed = 1;
|
277
|
+
wrapper->reconnect_enabled = 0;
|
219
278
|
wrapper->active_thread = Qnil;
|
220
279
|
}
|
221
280
|
|
@@ -234,7 +293,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
234
293
|
|
235
294
|
if (wrapper->refcount == 0) {
|
236
295
|
#ifndef _WIN32
|
237
|
-
if (wrapper
|
296
|
+
if (CONNECTED(wrapper) && !wrapper->automatic_close) {
|
238
297
|
/* The client is being garbage collected while connected. Prevent
|
239
298
|
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
240
299
|
* the socket by invalidating it. invalidate_fd() will drop this
|
@@ -246,10 +305,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
246
305
|
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
247
306
|
close(wrapper->client->net.fd);
|
248
307
|
}
|
308
|
+
wrapper->client->net.fd = -1;
|
249
309
|
}
|
250
310
|
#endif
|
251
311
|
|
252
312
|
nogvl_close(wrapper);
|
313
|
+
xfree(wrapper->client);
|
253
314
|
xfree(wrapper);
|
254
315
|
}
|
255
316
|
}
|
@@ -264,9 +325,9 @@ static VALUE allocate(VALUE klass) {
|
|
264
325
|
wrapper->server_version = 0;
|
265
326
|
wrapper->reconnect_enabled = 0;
|
266
327
|
wrapper->connect_timeout = 0;
|
267
|
-
wrapper->connected = 0; /* means that a database connection is open */
|
268
328
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
269
329
|
wrapper->refcount = 1;
|
330
|
+
wrapper->closed = 0;
|
270
331
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
271
332
|
|
272
333
|
return obj;
|
@@ -397,7 +458,6 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
397
458
|
}
|
398
459
|
|
399
460
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
400
|
-
wrapper->connected = 1;
|
401
461
|
return self;
|
402
462
|
}
|
403
463
|
|
@@ -412,13 +472,23 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
412
472
|
static VALUE rb_mysql_client_close(VALUE self) {
|
413
473
|
GET_CLIENT(self);
|
414
474
|
|
415
|
-
if (wrapper->
|
475
|
+
if (wrapper->client) {
|
416
476
|
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
417
477
|
}
|
418
478
|
|
419
479
|
return Qnil;
|
420
480
|
}
|
421
481
|
|
482
|
+
/* call-seq:
|
483
|
+
* client.closed?
|
484
|
+
*
|
485
|
+
* @return [Boolean]
|
486
|
+
*/
|
487
|
+
static VALUE rb_mysql_client_closed(VALUE self) {
|
488
|
+
GET_CLIENT(self);
|
489
|
+
return CONNECTED(wrapper) ? Qfalse : Qtrue;
|
490
|
+
}
|
491
|
+
|
422
492
|
/*
|
423
493
|
* mysql_send_query is unlikely to block since most queries are small
|
424
494
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
@@ -451,7 +521,7 @@ static VALUE do_send_query(void *args) {
|
|
451
521
|
*/
|
452
522
|
static void *nogvl_read_query_result(void *ptr) {
|
453
523
|
MYSQL * client = ptr;
|
454
|
-
|
524
|
+
bool res = mysql_read_query_result(client);
|
455
525
|
|
456
526
|
return (void *)(res == 0 ? Qtrue : Qfalse);
|
457
527
|
}
|
@@ -538,14 +608,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
538
608
|
GET_CLIENT(self);
|
539
609
|
|
540
610
|
wrapper->active_thread = Qnil;
|
541
|
-
wrapper->connected = 0;
|
542
611
|
|
543
612
|
/* Invalidate the MySQL socket to prevent further communication.
|
544
613
|
* The GC will come along later and call mysql_close to free it.
|
545
614
|
*/
|
546
|
-
if (
|
547
|
-
|
548
|
-
|
615
|
+
if (CONNECTED(wrapper)) {
|
616
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
617
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
618
|
+
close(wrapper->client->net.fd);
|
619
|
+
}
|
620
|
+
wrapper->client->net.fd = -1;
|
549
621
|
}
|
550
622
|
|
551
623
|
rb_exc_raise(error);
|
@@ -594,26 +666,32 @@ static VALUE do_query(void *args) {
|
|
594
666
|
|
595
667
|
return Qnil;
|
596
668
|
}
|
597
|
-
#
|
598
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
599
|
-
VALUE self = args;
|
600
|
-
MYSQL_RES *result;
|
669
|
+
#endif
|
601
670
|
|
671
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
602
672
|
GET_CLIENT(self);
|
603
673
|
|
674
|
+
/* Check if execution terminated while result was still being read. */
|
604
675
|
if (!NIL_P(wrapper->active_thread)) {
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
676
|
+
if (CONNECTED(wrapper)) {
|
677
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
678
|
+
#ifndef _WIN32
|
679
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
680
|
+
rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
|
681
|
+
close(wrapper->client->net.fd);
|
682
|
+
}
|
683
|
+
#else
|
684
|
+
close(wrapper->client->net.fd);
|
685
|
+
#endif
|
686
|
+
wrapper->client->net.fd = -1;
|
687
|
+
}
|
688
|
+
/* Skip mysql client check performed before command execution. */
|
689
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
611
690
|
wrapper->active_thread = Qnil;
|
612
691
|
}
|
613
692
|
|
614
693
|
return Qnil;
|
615
694
|
}
|
616
|
-
#endif
|
617
695
|
|
618
696
|
void rb_mysql_client_set_active_thread(VALUE self) {
|
619
697
|
VALUE thread_current = rb_thread_current();
|
@@ -707,13 +785,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
707
785
|
|
708
786
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
709
787
|
|
710
|
-
return rb_mysql_client_async_result
|
788
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
711
789
|
}
|
712
790
|
#else
|
713
791
|
do_send_query(&args);
|
714
792
|
|
715
793
|
/* this will just block until the result is ready */
|
716
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
794
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
717
795
|
#endif
|
718
796
|
}
|
719
797
|
|
@@ -772,7 +850,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
772
850
|
const void *retval = NULL;
|
773
851
|
unsigned int intval = 0;
|
774
852
|
const char * charval = NULL;
|
775
|
-
|
853
|
+
bool boolval;
|
776
854
|
|
777
855
|
GET_CLIENT(self);
|
778
856
|
|
@@ -807,10 +885,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
807
885
|
retval = &boolval;
|
808
886
|
break;
|
809
887
|
|
888
|
+
#ifdef MYSQL_SECURE_AUTH
|
810
889
|
case MYSQL_SECURE_AUTH:
|
811
890
|
boolval = (value == Qfalse ? 0 : 1);
|
812
891
|
retval = &boolval;
|
813
892
|
break;
|
893
|
+
#endif
|
814
894
|
|
815
895
|
case MYSQL_READ_DEFAULT_FILE:
|
816
896
|
charval = (const char *)StringValueCStr(value);
|
@@ -827,6 +907,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
827
907
|
retval = charval;
|
828
908
|
break;
|
829
909
|
|
910
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
911
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
912
|
+
boolval = (value == Qfalse ? 0 : 1);
|
913
|
+
retval = &boolval;
|
914
|
+
break;
|
915
|
+
#endif
|
916
|
+
|
830
917
|
default:
|
831
918
|
return Qfalse;
|
832
919
|
}
|
@@ -1016,7 +1103,7 @@ static void *nogvl_ping(void *ptr) {
|
|
1016
1103
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
1017
1104
|
GET_CLIENT(self);
|
1018
1105
|
|
1019
|
-
if (!wrapper
|
1106
|
+
if (!CONNECTED(wrapper)) {
|
1020
1107
|
return Qfalse;
|
1021
1108
|
} else {
|
1022
1109
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
@@ -1192,6 +1279,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1192
1279
|
#endif
|
1193
1280
|
GET_CLIENT(self);
|
1194
1281
|
|
1282
|
+
Check_Type(value, T_STRING);
|
1195
1283
|
charset_name = RSTRING_PTR(value);
|
1196
1284
|
|
1197
1285
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -1228,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1228
1316
|
}
|
1229
1317
|
|
1230
1318
|
static VALUE set_secure_auth(VALUE self, VALUE value) {
|
1319
|
+
/* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
|
1320
|
+
#ifdef MYSQL_SECURE_AUTH
|
1231
1321
|
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
|
1322
|
+
#else
|
1323
|
+
return Qfalse;
|
1324
|
+
#endif
|
1232
1325
|
}
|
1233
1326
|
|
1234
1327
|
static VALUE set_read_default_file(VALUE self, VALUE value) {
|
@@ -1243,6 +1336,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1243
1336
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1244
1337
|
}
|
1245
1338
|
|
1339
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1340
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1341
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1342
|
+
#else
|
1343
|
+
rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
|
1344
|
+
#endif
|
1345
|
+
}
|
1346
|
+
|
1246
1347
|
static VALUE initialize_ext(VALUE self) {
|
1247
1348
|
GET_CLIENT(self);
|
1248
1349
|
|
@@ -1303,6 +1404,7 @@ void init_mysql2_client() {
|
|
1303
1404
|
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1304
1405
|
|
1305
1406
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1407
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1306
1408
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1307
1409
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1308
1410
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
@@ -1337,6 +1439,8 @@ void init_mysql2_client() {
|
|
1337
1439
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1338
1440
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1339
1441
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1442
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1443
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1340
1444
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1341
1445
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1342
1446
|
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
@@ -1358,6 +1462,10 @@ void init_mysql2_client() {
|
|
1358
1462
|
#ifdef CLIENT_LONG_PASSWORD
|
1359
1463
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1360
1464
|
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1465
|
+
#else
|
1466
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1467
|
+
* but we're using it in our default connection flags. */
|
1468
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
1361
1469
|
#endif
|
1362
1470
|
|
1363
1471
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1464,4 +1572,31 @@ void init_mysql2_client() {
|
|
1464
1572
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1465
1573
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1466
1574
|
#endif
|
1575
|
+
|
1576
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1577
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1578
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1579
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1580
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1581
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1582
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1583
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1584
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1585
|
+
#endif
|
1586
|
+
|
1587
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1588
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1589
|
+
#endif
|
1590
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1591
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1592
|
+
#endif
|
1593
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1594
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1595
|
+
#endif
|
1596
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1597
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1598
|
+
#endif
|
1599
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1600
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1601
|
+
#endif
|
1467
1602
|
}
|
data/ext/mysql2/client.h
CHANGED
@@ -44,19 +44,12 @@ 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
|
49
|
+
int closed;
|
51
50
|
MYSQL *client;
|
52
51
|
} mysql_client_wrapper;
|
53
52
|
|
54
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
55
|
-
REQUIRE_INITIALIZED(wrapper) \
|
56
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
57
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
58
|
-
}
|
59
|
-
|
60
53
|
void rb_mysql_client_set_active_thread(VALUE self);
|
61
54
|
|
62
55
|
#define GET_CLIENT(self) \
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -12,6 +12,20 @@ def asplode(lib)
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def add_ssl_defines(header)
|
16
|
+
all_modes_found = %w(SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY).inject(true) do |m, ssl_mode|
|
17
|
+
m && have_const(ssl_mode, header)
|
18
|
+
end
|
19
|
+
$CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
|
20
|
+
# if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
|
21
|
+
has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
|
22
|
+
$CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
|
23
|
+
end
|
24
|
+
|
25
|
+
# 2.1+
|
26
|
+
have_func('rb_absint_size')
|
27
|
+
have_func('rb_absint_singlebit_p')
|
28
|
+
|
15
29
|
# 2.0-only
|
16
30
|
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
17
31
|
|
@@ -20,6 +34,7 @@ have_func('rb_thread_blocking_region')
|
|
20
34
|
have_func('rb_wait_for_single_fd')
|
21
35
|
have_func('rb_hash_dup')
|
22
36
|
have_func('rb_intern3')
|
37
|
+
have_func('rb_big_cmp')
|
23
38
|
|
24
39
|
# borrowed from mysqlplus
|
25
40
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
@@ -37,6 +52,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
|
|
37
52
|
/usr/local/opt/mysql5*
|
38
53
|
).map { |dir| dir << '/bin' }
|
39
54
|
|
55
|
+
# For those without HOMEBREW_ROOT in PATH
|
56
|
+
dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
|
57
|
+
|
40
58
|
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
|
41
59
|
|
42
60
|
# If the user has provided a --with-mysql-dir argument, we must respect it or fail.
|
@@ -87,11 +105,17 @@ else
|
|
87
105
|
asplode 'mysql.h'
|
88
106
|
end
|
89
107
|
|
90
|
-
%w(errmsg.h
|
91
|
-
header = [prefix, h].compact.join
|
108
|
+
%w(errmsg.h).each do |h|
|
109
|
+
header = [prefix, h].compact.join('/')
|
92
110
|
asplode h unless have_header header
|
93
111
|
end
|
94
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
|
+
have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
|
118
|
+
|
95
119
|
# This is our wishlist. We use whichever flags work on the host.
|
96
120
|
# -Wall and -Wextra are included by default.
|
97
121
|
wishlist = [
|
data/ext/mysql2/mysql2_ext.h
CHANGED
@@ -11,14 +11,10 @@ void Init_mysql2(void);
|
|
11
11
|
|
12
12
|
#ifdef HAVE_MYSQL_H
|
13
13
|
#include <mysql.h>
|
14
|
-
#include <mysql_com.h>
|
15
14
|
#include <errmsg.h>
|
16
|
-
#include <mysqld_error.h>
|
17
15
|
#else
|
18
16
|
#include <mysql/mysql.h>
|
19
|
-
#include <mysql/mysql_com.h>
|
20
17
|
#include <mysql/errmsg.h>
|
21
|
-
#include <mysql/mysqld_error.h>
|
22
18
|
#endif
|
23
19
|
|
24
20
|
#ifdef HAVE_RUBY_ENCODING_H
|