mysql2 0.4.1 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +58 -15
- data/ext/mysql2/client.c +190 -50
- data/ext/mysql2/client.h +1 -10
- data/ext/mysql2/extconf.rb +59 -15
- data/ext/mysql2/result.c +52 -37
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +118 -35
- data/ext/mysql2/statement.h +1 -0
- data/lib/mysql2/client.rb +42 -7
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/mysql2/client_spec.rb +124 -70
- data/spec/mysql2/error_spec.rb +1 -1
- data/spec/mysql2/result_spec.rb +9 -0
- data/spec/mysql2/statement_spec.rb +81 -6
- data/spec/ssl/gen_certs.sh +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f6e6398c4050512cbe22c6994153fb0ab4b3d89
|
4
|
+
data.tar.gz: 8e6345e8df7b550c7248f86cb374422866ffdd38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b820a05878ea935e6a25b4b1e34bfebaf47fc48a8ef24ff8f8920cc4ecaabfc2f0ba7cd590c8d4c51632d5e564fba11ae35880e0bf3230425a7264341e2625a
|
7
|
+
data.tar.gz: 32f8d9a642c643225386883f97305be79868984a55a541358983ab70ec07f421dc999e8a656f51044c089a2aecb697d2b217453cf2ce0ca9fe4a17f313131e8e
|
data/README.md
CHANGED
@@ -58,6 +58,20 @@ This may be needed if you deploy to a system where these libraries
|
|
58
58
|
are located somewhere different than on your build system.
|
59
59
|
This overrides any rpath calculated by default or by the options above.
|
60
60
|
|
61
|
+
* `--with-sanitize[=address,cfi,integer,memory,thread,undefined]` -
|
62
|
+
Enable sanitizers for Clang / GCC. If no argument is given, try to enable
|
63
|
+
all sanitizers or fail if none are available. If a command-separated list of
|
64
|
+
specific sanitizers is given, configure will fail unless they all are available.
|
65
|
+
Note that the some sanitizers may incur a performance penalty, and the Address
|
66
|
+
Sanitizer may require a runtime library.
|
67
|
+
To see line numbers in backtraces, declare these environment variables
|
68
|
+
(adjust the llvm-symbolizer path as needed for your system):
|
69
|
+
|
70
|
+
``` sh
|
71
|
+
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
|
72
|
+
export ASAN_OPTIONS=symbolize=1
|
73
|
+
```
|
74
|
+
|
61
75
|
### Linux and other Unixes
|
62
76
|
|
63
77
|
You may need to install a package such as `libmysqlclient-dev` or `mysql-devel`;
|
@@ -71,6 +85,9 @@ You may use MacPorts, Homebrew, or a native MySQL installer package. The most
|
|
71
85
|
common paths will be automatically searched. If you want to select a specific
|
72
86
|
MySQL directory, use the `--with-mysql-dir` or `--with-mysql-config` options above.
|
73
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
|
+
|
74
91
|
### Windows
|
75
92
|
Make sure that you have Ruby and the DevKit compilers installed. We recommend
|
76
93
|
the [Ruby Installer](http://rubyinstaller.org) distribution.
|
@@ -150,8 +167,8 @@ by the query like this:
|
|
150
167
|
``` ruby
|
151
168
|
headers = results.fields # <= that's an array of field names, in order
|
152
169
|
results.each(:as => :array) do |row|
|
153
|
-
# Each row is an array, ordered the same as the query results
|
154
|
-
# 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"
|
155
172
|
end
|
156
173
|
```
|
157
174
|
|
@@ -189,6 +206,7 @@ Mysql2::Client.new(
|
|
189
206
|
:reconnect = true/false,
|
190
207
|
:local_infile = true/false,
|
191
208
|
:secure_auth = true/false,
|
209
|
+
:ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
|
192
210
|
:default_file = '/path/to/my.cfg',
|
193
211
|
:default_group = 'my.cfg section',
|
194
212
|
:init_command => sql
|
@@ -258,15 +276,26 @@ Yields:
|
|
258
276
|
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
259
277
|
```
|
260
278
|
|
261
|
-
See https://gist.github.com/1367987 for using MULTI_STATEMENTS with Active Record.
|
262
|
-
|
263
279
|
### Secure auth
|
264
280
|
|
265
281
|
Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
|
266
282
|
When secure_auth is enabled, the server will refuse a connection if the account password is stored in old pre-MySQL 4.1 format.
|
267
283
|
The MySQL 5.6.5 client library may also refuse to attempt a connection if provided an older format password.
|
268
|
-
To bypass this restriction in the client, pass the option
|
269
|
-
|
284
|
+
To bypass this restriction in the client, pass the option `:secure_auth => false` to Mysql2::Client.new().
|
285
|
+
|
286
|
+
### Flags option parsing
|
287
|
+
|
288
|
+
The `:flags` parameter accepts an integer, a string, or an array. The integer
|
289
|
+
form allows the client to assemble flags from constants defined under
|
290
|
+
`Mysql2::Client` such as `Mysql2::Client::FOUND_ROWS`. Use a bitwise `|` (OR)
|
291
|
+
to specify several flags.
|
292
|
+
|
293
|
+
The string form will be split on whitespace and parsed as with the array form:
|
294
|
+
Plain flags are added to the default flags, while flags prefixed with `-`
|
295
|
+
(minus) are removed from the default flags.
|
296
|
+
|
297
|
+
This allows easier use with ActiveRecord's database.yml, avoiding the need for magic flag numbers.
|
298
|
+
For example, to disable protocol compression, and enable multiple statements and result sets:
|
270
299
|
|
271
300
|
``` yaml
|
272
301
|
development:
|
@@ -277,13 +306,17 @@ development:
|
|
277
306
|
password: my_password
|
278
307
|
host: 127.0.0.1
|
279
308
|
port: 3306
|
309
|
+
flags:
|
310
|
+
- -COMPRESS
|
311
|
+
- FOUND_ROWS
|
312
|
+
- MULTI_STATEMENTS
|
280
313
|
secure_auth: false
|
281
314
|
```
|
282
315
|
|
283
316
|
### Reading a MySQL config file
|
284
317
|
|
285
318
|
You may read configuration options from a MySQL configuration file by passing
|
286
|
-
the `:default_file` and `:default_group`
|
319
|
+
the `:default_file` and `:default_group` parameters. For example:
|
287
320
|
|
288
321
|
``` ruby
|
289
322
|
Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
@@ -291,7 +324,7 @@ Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
|
291
324
|
|
292
325
|
### Initial command on connect and reconnect
|
293
326
|
|
294
|
-
If you specify the init_command option, the SQL string you provide will be executed after the connection is established.
|
327
|
+
If you specify the `:init_command` option, the SQL string you provide will be executed after the connection is established.
|
295
328
|
If `:reconnect` is set to `true`, init_command will also be executed after a successful reconnect.
|
296
329
|
It is useful if you want to provide session options which survive reconnection.
|
297
330
|
|
@@ -369,6 +402,15 @@ client = Mysql2::Client.new
|
|
369
402
|
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
370
403
|
```
|
371
404
|
|
405
|
+
Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
|
406
|
+
|
407
|
+
``` ruby
|
408
|
+
client = Mysql2::Client.new
|
409
|
+
result = client.query("SELECT true", :cast_booleans => true)
|
410
|
+
```
|
411
|
+
|
412
|
+
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).
|
413
|
+
|
372
414
|
### Skipping casting
|
373
415
|
|
374
416
|
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.)
|
@@ -455,20 +497,21 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
455
497
|
|
456
498
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
457
499
|
|
458
|
-
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x
|
500
|
+
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
|
459
501
|
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
460
|
-
* Rubinius 2.x
|
502
|
+
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
461
503
|
|
462
504
|
This gem is tested with the following MySQL and MariaDB versions:
|
463
505
|
|
464
|
-
* MySQL 5.5, 5.7
|
506
|
+
* MySQL 5.5, 5.6, 5.7, 8.0
|
465
507
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
466
|
-
* MariaDB 5.5, 10.0
|
508
|
+
* MariaDB 5.5, 10.0, 10.1
|
467
509
|
|
468
|
-
### Active Record
|
510
|
+
### Ruby on Rails / Active Record
|
469
511
|
|
470
|
-
* mysql2 0.
|
471
|
-
* mysql2 0.3.x
|
512
|
+
* mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
|
513
|
+
* mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
|
514
|
+
* mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
|
472
515
|
|
473
516
|
### Asynchronous Active Record
|
474
517
|
|
data/ext/mysql2/client.c
CHANGED
@@ -30,6 +30,12 @@ VALUE rb_hash_dup(VALUE other) {
|
|
30
30
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
31
|
}
|
32
32
|
|
33
|
+
#define REQUIRE_CONNECTED(wrapper) \
|
34
|
+
REQUIRE_INITIALIZED(wrapper) \
|
35
|
+
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
36
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
37
|
+
}
|
38
|
+
|
33
39
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
34
40
|
REQUIRE_INITIALIZED(wrapper) \
|
35
41
|
if (wrapper->connected) { \
|
@@ -47,6 +53,16 @@ VALUE rb_hash_dup(VALUE other) {
|
|
47
53
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
48
54
|
#endif
|
49
55
|
|
56
|
+
/*
|
57
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
58
|
+
*/
|
59
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
60
|
+
#define SSL_MODE_DISABLED 1
|
61
|
+
#define SSL_MODE_REQUIRED 3
|
62
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
63
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
64
|
+
#endif
|
65
|
+
|
50
66
|
/*
|
51
67
|
* used to pass all arguments to mysql_real_connect while inside
|
52
68
|
* rb_thread_call_without_gvl
|
@@ -83,6 +99,43 @@ struct nogvl_select_db_args {
|
|
83
99
|
char *db;
|
84
100
|
};
|
85
101
|
|
102
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
103
|
+
unsigned long version = mysql_get_client_version();
|
104
|
+
|
105
|
+
if (version < 50703) {
|
106
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
107
|
+
return Qnil;
|
108
|
+
}
|
109
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
110
|
+
GET_CLIENT(self);
|
111
|
+
int val = NUM2INT( setting );
|
112
|
+
if (version >= 50703 && version < 50711) {
|
113
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
114
|
+
bool b = ( val == SSL_MODE_REQUIRED );
|
115
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
116
|
+
return INT2NUM(result);
|
117
|
+
|
118
|
+
} else {
|
119
|
+
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
120
|
+
return Qnil;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
#endif
|
124
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
125
|
+
GET_CLIENT(self);
|
126
|
+
int val = NUM2INT( setting );
|
127
|
+
|
128
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
129
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
130
|
+
}
|
131
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
132
|
+
|
133
|
+
return INT2NUM(result);
|
134
|
+
#endif
|
135
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
136
|
+
return Qnil;
|
137
|
+
#endif
|
138
|
+
}
|
86
139
|
/*
|
87
140
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
88
141
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -133,7 +186,7 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
133
186
|
|
134
187
|
static void *nogvl_init(void *ptr) {
|
135
188
|
MYSQL *client;
|
136
|
-
mysql_client_wrapper *wrapper =
|
189
|
+
mysql_client_wrapper *wrapper = ptr;
|
137
190
|
|
138
191
|
/* may initialize embedded server and read /etc/services off disk */
|
139
192
|
client = mysql_init(wrapper->client);
|
@@ -213,6 +266,7 @@ static void *nogvl_close(void *ptr) {
|
|
213
266
|
|
214
267
|
if (wrapper->client) {
|
215
268
|
mysql_close(wrapper->client);
|
269
|
+
xfree(wrapper->client);
|
216
270
|
wrapper->client = NULL;
|
217
271
|
wrapper->connected = 0;
|
218
272
|
wrapper->active_thread = Qnil;
|
@@ -223,7 +277,7 @@ static void *nogvl_close(void *ptr) {
|
|
223
277
|
|
224
278
|
/* this is called during GC */
|
225
279
|
static void rb_mysql_client_free(void *ptr) {
|
226
|
-
mysql_client_wrapper *wrapper =
|
280
|
+
mysql_client_wrapper *wrapper = ptr;
|
227
281
|
decr_mysql2_client(wrapper);
|
228
282
|
}
|
229
283
|
|
@@ -233,7 +287,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
233
287
|
|
234
288
|
if (wrapper->refcount == 0) {
|
235
289
|
#ifndef _WIN32
|
236
|
-
if (wrapper->connected) {
|
290
|
+
if (wrapper->connected && !wrapper->automatic_close) {
|
237
291
|
/* The client is being garbage collected while connected. Prevent
|
238
292
|
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
239
293
|
* the socket by invalidating it. invalidate_fd() will drop this
|
@@ -249,7 +303,6 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
249
303
|
#endif
|
250
304
|
|
251
305
|
nogvl_close(wrapper);
|
252
|
-
xfree(wrapper->client);
|
253
306
|
xfree(wrapper);
|
254
307
|
}
|
255
308
|
}
|
@@ -259,7 +312,8 @@ static VALUE allocate(VALUE klass) {
|
|
259
312
|
mysql_client_wrapper * wrapper;
|
260
313
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
261
314
|
wrapper->encoding = Qnil;
|
262
|
-
|
315
|
+
wrapper->active_thread = Qnil;
|
316
|
+
wrapper->automatic_close = 1;
|
263
317
|
wrapper->server_version = 0;
|
264
318
|
wrapper->reconnect_enabled = 0;
|
265
319
|
wrapper->connect_timeout = 0;
|
@@ -331,6 +385,26 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
331
385
|
return rb_str;
|
332
386
|
}
|
333
387
|
|
388
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
389
|
+
{
|
390
|
+
const char *cipher;
|
391
|
+
VALUE rb_str;
|
392
|
+
GET_CLIENT(self);
|
393
|
+
|
394
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
395
|
+
|
396
|
+
if (cipher == NULL) {
|
397
|
+
return Qnil;
|
398
|
+
}
|
399
|
+
|
400
|
+
rb_str = rb_str_new2(cipher);
|
401
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
402
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
403
|
+
#endif
|
404
|
+
|
405
|
+
return rb_str;
|
406
|
+
}
|
407
|
+
|
334
408
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
335
409
|
struct nogvl_connect_args args;
|
336
410
|
time_t start_time, end_time, elapsed_time, connect_timeout;
|
@@ -372,7 +446,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
372
446
|
if (wrapper->connect_timeout)
|
373
447
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
374
448
|
if (rv == Qfalse)
|
375
|
-
|
449
|
+
rb_raise_mysql2_error(wrapper);
|
376
450
|
}
|
377
451
|
|
378
452
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
@@ -381,13 +455,12 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
381
455
|
}
|
382
456
|
|
383
457
|
/*
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
387
|
-
*
|
458
|
+
* Immediately disconnect from the server; normally the garbage collector
|
459
|
+
* will disconnect automatically when a connection is no longer needed.
|
460
|
+
* Explicitly closing this will free up server resources sooner than waiting
|
461
|
+
* for the garbage collector.
|
388
462
|
*
|
389
|
-
* @
|
390
|
-
* @return [void]
|
463
|
+
* @return [nil]
|
391
464
|
*/
|
392
465
|
static VALUE rb_mysql_client_close(VALUE self) {
|
393
466
|
GET_CLIENT(self);
|
@@ -418,8 +491,8 @@ static VALUE do_send_query(void *args) {
|
|
418
491
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
419
492
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
420
493
|
/* an error occurred, we're not active anymore */
|
421
|
-
|
422
|
-
|
494
|
+
wrapper->active_thread = Qnil;
|
495
|
+
rb_raise_mysql2_error(wrapper);
|
423
496
|
}
|
424
497
|
return Qnil;
|
425
498
|
}
|
@@ -431,16 +504,15 @@ static VALUE do_send_query(void *args) {
|
|
431
504
|
*/
|
432
505
|
static void *nogvl_read_query_result(void *ptr) {
|
433
506
|
MYSQL * client = ptr;
|
434
|
-
|
507
|
+
bool res = mysql_read_query_result(client);
|
435
508
|
|
436
509
|
return (void *)(res == 0 ? Qtrue : Qfalse);
|
437
510
|
}
|
438
511
|
|
439
512
|
static void *nogvl_do_result(void *ptr, char use_result) {
|
440
|
-
mysql_client_wrapper *wrapper;
|
513
|
+
mysql_client_wrapper *wrapper = ptr;
|
441
514
|
MYSQL_RES *result;
|
442
515
|
|
443
|
-
wrapper = (mysql_client_wrapper *)ptr;
|
444
516
|
if (use_result) {
|
445
517
|
result = mysql_use_result(wrapper->client);
|
446
518
|
} else {
|
@@ -449,7 +521,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
449
521
|
|
450
522
|
/* once our result is stored off, this connection is
|
451
523
|
ready for another command to be issued */
|
452
|
-
|
524
|
+
wrapper->active_thread = Qnil;
|
453
525
|
|
454
526
|
return result;
|
455
527
|
}
|
@@ -481,8 +553,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
481
553
|
REQUIRE_CONNECTED(wrapper);
|
482
554
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
483
555
|
/* an error occurred, mark this connection inactive */
|
484
|
-
|
485
|
-
|
556
|
+
wrapper->active_thread = Qnil;
|
557
|
+
rb_raise_mysql2_error(wrapper);
|
486
558
|
}
|
487
559
|
|
488
560
|
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
@@ -494,7 +566,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
494
566
|
|
495
567
|
if (result == NULL) {
|
496
568
|
if (mysql_errno(wrapper->client) != 0) {
|
497
|
-
|
569
|
+
wrapper->active_thread = Qnil;
|
498
570
|
rb_raise_mysql2_error(wrapper);
|
499
571
|
}
|
500
572
|
/* no data and no error, so query was not a SELECT */
|
@@ -518,29 +590,30 @@ struct async_query_args {
|
|
518
590
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
519
591
|
GET_CLIENT(self);
|
520
592
|
|
521
|
-
|
593
|
+
wrapper->active_thread = Qnil;
|
522
594
|
wrapper->connected = 0;
|
523
595
|
|
524
596
|
/* Invalidate the MySQL socket to prevent further communication.
|
525
597
|
* The GC will come along later and call mysql_close to free it.
|
526
598
|
*/
|
527
|
-
if (
|
528
|
-
|
529
|
-
|
599
|
+
if (wrapper->client) {
|
600
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
601
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
602
|
+
close(wrapper->client->net.fd);
|
603
|
+
}
|
530
604
|
}
|
531
605
|
|
532
606
|
rb_exc_raise(error);
|
533
607
|
}
|
534
608
|
|
535
609
|
static VALUE do_query(void *args) {
|
536
|
-
struct async_query_args *async_args;
|
610
|
+
struct async_query_args *async_args = args;
|
537
611
|
struct timeval tv;
|
538
|
-
struct timeval*
|
612
|
+
struct timeval *tvp;
|
539
613
|
long int sec;
|
540
614
|
int retval;
|
541
615
|
VALUE read_timeout;
|
542
616
|
|
543
|
-
async_args = (struct async_query_args *)args;
|
544
617
|
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
545
618
|
|
546
619
|
tvp = NULL;
|
@@ -576,28 +649,30 @@ static VALUE do_query(void *args) {
|
|
576
649
|
|
577
650
|
return Qnil;
|
578
651
|
}
|
579
|
-
#
|
580
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
581
|
-
VALUE self;
|
582
|
-
MYSQL_RES *result;
|
583
|
-
|
584
|
-
self = (VALUE)args;
|
652
|
+
#endif
|
585
653
|
|
654
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
586
655
|
GET_CLIENT(self);
|
587
656
|
|
657
|
+
/* Check if execution terminated while result was still being read. */
|
588
658
|
if (!NIL_P(wrapper->active_thread)) {
|
589
|
-
/*
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
659
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
660
|
+
#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
|
+
}
|
665
|
+
#else
|
666
|
+
close(wrapper->client->net.fd);
|
667
|
+
#endif
|
668
|
+
/* Skip mysql client check performed before command execution. */
|
669
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
670
|
+
wrapper->active_thread = Qnil;
|
671
|
+
wrapper->connected = 0;
|
596
672
|
}
|
597
673
|
|
598
674
|
return Qnil;
|
599
675
|
}
|
600
|
-
#endif
|
601
676
|
|
602
677
|
void rb_mysql_client_set_active_thread(VALUE self) {
|
603
678
|
VALUE thread_current = rb_thread_current();
|
@@ -691,13 +766,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
691
766
|
|
692
767
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
693
768
|
|
694
|
-
return rb_mysql_client_async_result
|
769
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
695
770
|
}
|
696
771
|
#else
|
697
772
|
do_send_query(&args);
|
698
773
|
|
699
774
|
/* this will just block until the result is ready */
|
700
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
775
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
701
776
|
#endif
|
702
777
|
}
|
703
778
|
|
@@ -756,7 +831,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
756
831
|
const void *retval = NULL;
|
757
832
|
unsigned int intval = 0;
|
758
833
|
const char * charval = NULL;
|
759
|
-
|
834
|
+
bool boolval;
|
760
835
|
|
761
836
|
GET_CLIENT(self);
|
762
837
|
|
@@ -1015,10 +1090,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1015
1090
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1016
1091
|
{
|
1017
1092
|
GET_CLIENT(self);
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1093
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1094
|
+
return Qfalse;
|
1095
|
+
else
|
1096
|
+
return Qtrue;
|
1022
1097
|
}
|
1023
1098
|
|
1024
1099
|
/* call-seq:
|
@@ -1085,6 +1160,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1085
1160
|
}
|
1086
1161
|
#endif
|
1087
1162
|
|
1163
|
+
/* call-seq:
|
1164
|
+
* client.automatic_close?
|
1165
|
+
*
|
1166
|
+
* @return [Boolean]
|
1167
|
+
*/
|
1168
|
+
static VALUE get_automatic_close(VALUE self) {
|
1169
|
+
GET_CLIENT(self);
|
1170
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
/* call-seq:
|
1174
|
+
* client.automatic_close = false
|
1175
|
+
*
|
1176
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1177
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1178
|
+
* call +close+ when the connection is no longer needed.
|
1179
|
+
*
|
1180
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1181
|
+
*/
|
1182
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1183
|
+
GET_CLIENT(self);
|
1184
|
+
if (RTEST(value)) {
|
1185
|
+
wrapper->automatic_close = 1;
|
1186
|
+
} else {
|
1187
|
+
#ifndef _WIN32
|
1188
|
+
wrapper->automatic_close = 0;
|
1189
|
+
#else
|
1190
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1191
|
+
#endif
|
1192
|
+
}
|
1193
|
+
return value;
|
1194
|
+
}
|
1195
|
+
|
1088
1196
|
/* call-seq:
|
1089
1197
|
* client.reconnect = true
|
1090
1198
|
*
|
@@ -1143,6 +1251,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1143
1251
|
#endif
|
1144
1252
|
GET_CLIENT(self);
|
1145
1253
|
|
1254
|
+
Check_Type(value, T_STRING);
|
1146
1255
|
charset_name = RSTRING_PTR(value);
|
1147
1256
|
|
1148
1257
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -1199,7 +1308,7 @@ static VALUE initialize_ext(VALUE self) {
|
|
1199
1308
|
|
1200
1309
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1201
1310
|
/* TODO: warning - not enough memory? */
|
1202
|
-
|
1311
|
+
rb_raise_mysql2_error(wrapper);
|
1203
1312
|
}
|
1204
1313
|
|
1205
1314
|
wrapper->initialized = 1;
|
@@ -1268,9 +1377,12 @@ void init_mysql2_client() {
|
|
1268
1377
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1269
1378
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1270
1379
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1380
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1381
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1271
1382
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1272
1383
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1273
1384
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1385
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1274
1386
|
#ifdef HAVE_RUBY_ENCODING_H
|
1275
1387
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1276
1388
|
#endif
|
@@ -1285,6 +1397,7 @@ void init_mysql2_client() {
|
|
1285
1397
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1286
1398
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1287
1399
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1400
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1288
1401
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1289
1402
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1290
1403
|
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
@@ -1412,4 +1525,31 @@ void init_mysql2_client() {
|
|
1412
1525
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1413
1526
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1414
1527
|
#endif
|
1528
|
+
|
1529
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1530
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1531
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1532
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1533
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1534
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1535
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1536
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1537
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1538
|
+
#endif
|
1539
|
+
|
1540
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1541
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1542
|
+
#endif
|
1543
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1544
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1545
|
+
#endif
|
1546
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1547
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1548
|
+
#endif
|
1549
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1550
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1551
|
+
#endif
|
1552
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1553
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1554
|
+
#endif
|
1415
1555
|
}
|