mysql2 0.4.1 → 0.4.6
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 +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
|
}
|