mysql2 0.3.15 → 0.3.16
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 +65 -62
- data/ext/mysql2/client.c +107 -26
- data/ext/mysql2/client.h +1 -0
- data/ext/mysql2/extconf.rb +2 -2
- data/ext/mysql2/infile.c +1 -0
- data/ext/mysql2/mysql2_ext.h +0 -1
- data/lib/mysql2/client.rb +4 -1
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +57 -9
- 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: 24297b49b4e7641f01f0b35438a160e8667c6b3b
|
4
|
+
data.tar.gz: ccea57bd6d82f744ebb04420791297bcb7a0bfa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a5c9e85d85d0bbc945b5c723cf437f2ad171a1f6b086f7e92fc18d43cae1a92c1417ec0e3e027c33fc0d78ac79ba4738a7415df5d6776fbe0720e9814976b95
|
7
|
+
data.tar.gz: bb180191c3ffa181a66e1fc801545374bca55dd2512f63e892a78b57ed6f9cd5862fa0213a44349d94b46cd29619305037e16433126aed0241049303d9bd614e
|
data/README.md
CHANGED
@@ -10,9 +10,9 @@ It also forces the use of UTF-8 [or binary] for the connection [and all strings
|
|
10
10
|
|
11
11
|
The API consists of two classes:
|
12
12
|
|
13
|
-
Mysql2::Client - your connection to the database
|
13
|
+
`Mysql2::Client` - your connection to the database
|
14
14
|
|
15
|
-
Mysql2::Result - returned from issuing a #query on the connection. It includes Enumerable.
|
15
|
+
`Mysql2::Result` - returned from issuing a #query on the connection. It includes Enumerable.
|
16
16
|
|
17
17
|
## Installing
|
18
18
|
### OSX / Linux
|
@@ -119,8 +119,7 @@ end
|
|
119
119
|
How about with symbolized keys?
|
120
120
|
|
121
121
|
``` ruby
|
122
|
-
|
123
|
-
client.query("SELECT * FROM users WHERE group='githubbers'").each(:symbolize_keys => true) do |row|
|
122
|
+
client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true) do |row|
|
124
123
|
# do something with row, it's ready to rock
|
125
124
|
end
|
126
125
|
```
|
@@ -157,10 +156,12 @@ Mysql2::Client.new(
|
|
157
156
|
:local_infile = true/false,
|
158
157
|
:secure_auth = true/false,
|
159
158
|
:default_file = '/path/to/my.cfg',
|
160
|
-
:default_group = 'my.cfg section'
|
159
|
+
:default_group = 'my.cfg section',
|
160
|
+
:init_command => sql
|
161
161
|
)
|
162
162
|
```
|
163
163
|
|
164
|
+
|
164
165
|
### SSL options
|
165
166
|
|
166
167
|
Setting any of the following options will enable an SSL connection, but only if
|
@@ -252,6 +253,15 @@ the `:default_file` and `:default_group` paramters. For example:
|
|
252
253
|
Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
253
254
|
```
|
254
255
|
|
256
|
+
### Initial command on connect and reconnect
|
257
|
+
|
258
|
+
If you specify the init_command option, the SQL string you provide will be executed after the connection is established.
|
259
|
+
If `:reconnect` is set to `true`, init_command will also be executed after a successful reconnect.
|
260
|
+
It is useful if you want to provide session options which survive reconnection.
|
261
|
+
|
262
|
+
``` ruby
|
263
|
+
Mysql2::Client.new(:init_command => "SET @@SESSION.sql_mode = 'STRICT_ALL_TABLES'")
|
264
|
+
```
|
255
265
|
|
256
266
|
## Cascading config
|
257
267
|
|
@@ -390,23 +400,50 @@ There are a few things that need to be kept in mind while using streaming:
|
|
390
400
|
|
391
401
|
Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html.
|
392
402
|
|
393
|
-
|
403
|
+
### Lazy Everything
|
404
|
+
|
405
|
+
Well... almost ;)
|
406
|
+
|
407
|
+
Field name strings/symbols are shared across all the rows so only one object is ever created to represent the field name for an entire dataset.
|
408
|
+
|
409
|
+
Rows themselves are lazily created in ruby-land when an attempt to yield it is made via #each.
|
410
|
+
For example, if you were to yield 4 rows from a 100 row dataset, only 4 hashes will be created. The rest will sit and wait in C-land until you want them (or when the GC goes to cleanup your `Mysql2::Result` instance).
|
411
|
+
Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
|
412
|
+
Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
|
413
|
+
|
414
|
+
This caching behavior can be disabled by setting the `:cache_rows` option to false.
|
415
|
+
|
416
|
+
As for field values themselves, I'm workin on it - but expect that soon.
|
417
|
+
|
418
|
+
## Compatibility
|
419
|
+
|
420
|
+
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
421
|
+
|
422
|
+
* Ruby MRI 1.8.7, 1.9.2, 1.9.3, 2.0.0, 2.1.x (ongoing patch releases)
|
423
|
+
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
424
|
+
* Rubinius 2.x
|
425
|
+
|
426
|
+
This gem is tested with the following MySQL and MariaDB versions:
|
427
|
+
|
428
|
+
* MySQL 5.0, 5.1, 5.5, 5.6
|
429
|
+
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
430
|
+
* MariaDB 5.5, 10.0
|
394
431
|
|
395
|
-
|
396
|
-
That was easy right? :)
|
432
|
+
### Active Record
|
397
433
|
|
398
|
-
|
399
|
-
|
434
|
+
* mysql2 0.2.x includes an Active Record driver compatible with AR 2.3 and 3.0
|
435
|
+
* mysql2 0.3.x does not include an AR driver because it is included in AR 3.1 and above
|
400
436
|
|
401
|
-
|
437
|
+
### Asynchronous Active Record
|
402
438
|
|
403
439
|
Please see the [em-synchrony](https://github.com/igrigorik/em-synchrony) project for details about using EventMachine with mysql2 and Rails.
|
404
440
|
|
405
|
-
|
441
|
+
### Sequel
|
406
442
|
|
407
|
-
|
443
|
+
Sequel includes a mysql2 adapter in all releases since 3.15 (2010-09-01).
|
444
|
+
Use the prefix "mysql2://" in your connection specification.
|
408
445
|
|
409
|
-
|
446
|
+
### EventMachine
|
410
447
|
|
411
448
|
The mysql2 EventMachine deferrable api allows you to make async queries using EventMachine,
|
412
449
|
while specifying callbacks for success for failure. Here's a simple example:
|
@@ -429,64 +466,30 @@ EM.run do
|
|
429
466
|
end
|
430
467
|
```
|
431
468
|
|
432
|
-
##
|
469
|
+
## Benchmarks and Comparison
|
433
470
|
|
434
|
-
|
435
|
-
|
436
|
-
Field name strings/symbols are shared across all the rows so only one object is ever created to represent the field name for an entire dataset.
|
437
|
-
|
438
|
-
Rows themselves are lazily created in ruby-land when an attempt to yield it is made via #each.
|
439
|
-
For example, if you were to yield 4 rows from a 100 row dataset, only 4 hashes will be created. The rest will sit and wait in C-land until you want them (or when the GC goes to cleanup your `Mysql2::Result` instance).
|
440
|
-
Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
|
441
|
-
Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
|
442
|
-
|
443
|
-
This caching behavior can be disabled by setting the :cache_rows option to false.
|
444
|
-
|
445
|
-
As for field values themselves, I'm workin on it - but expect that soon.
|
471
|
+
The mysql2 gem converts MySQL field types to Ruby data types in C code, providing a serious speed benefit.
|
446
472
|
|
447
|
-
|
448
|
-
|
449
|
-
This gem is regularly tested against the following Ruby versions on Linux and Mac OS X:
|
450
|
-
|
451
|
-
* Ruby MRI 1.8.7, 1.9.2, 1.9.3, 2.0.0 (ongoing patch releases).
|
452
|
-
* Ruby Enterprise Edition (based on MRI 1.8.7).
|
453
|
-
* Rubinius 2.0 in compatibility modes 1.8, 1.9, 2.0.
|
454
|
-
|
455
|
-
The mysql2 gem 0.2.x series includes an Active Record driver that works with AR
|
456
|
-
2.3.x and 3.0.x. Starting in Active Record 3.1, a mysql2 driver is included in
|
457
|
-
the Active Record codebase and no longer provided in mysql2 gem 0.3 and above.
|
458
|
-
|
459
|
-
## Yeah... but why?
|
460
|
-
|
461
|
-
Someone: Dude, the Mysql gem works fiiiiiine.
|
473
|
+
The do_mysql gem also converts MySQL fields types, but has a considerably more complex API and is still ~2x slower than mysql2.
|
462
474
|
|
463
|
-
|
464
|
-
them into proper Ruby types in Ruby-land - which is slow as balls.
|
475
|
+
The mysql gem returns only nil or string data types, leaving you to convert field values to Ruby types in Ruby-land, which is much slower than mysql2's C code.
|
465
476
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
## Benchmarks
|
471
|
-
|
472
|
-
Performing a basic "SELECT * FROM" query on a table with 30k rows and fields of nearly every Ruby-representable data type,
|
473
|
-
then iterating over every row using an #each like method yielding a block:
|
474
|
-
|
475
|
-
These results are from the `query_with_mysql_casting.rb` script in the benchmarks folder
|
477
|
+
For a comparative benchmark, the script below performs a basic "SELECT * FROM"
|
478
|
+
query on a table with 30k rows and fields of nearly every Ruby-representable
|
479
|
+
data type, then iterating over every row using an #each like method yielding a
|
480
|
+
block:
|
476
481
|
|
477
482
|
``` sh
|
478
|
-
|
479
|
-
Mysql2
|
480
|
-
|
481
|
-
|
482
|
-
1.650000 0.200000 1.850000 ( 2.811357)
|
483
|
-
Mysql
|
484
|
-
7.500000 0.210000 7.710000 ( 8.065871)
|
483
|
+
user system total real
|
484
|
+
Mysql2 0.750000 0.180000 0.930000 (1.821655)
|
485
|
+
do_mysql 1.650000 0.200000 1.850000 (2.811357)
|
486
|
+
Mysql 7.500000 0.210000 7.710000 (8.065871)
|
485
487
|
```
|
486
488
|
|
489
|
+
These results are from the `query_with_mysql_casting.rb` script in the benchmarks folder.
|
490
|
+
|
487
491
|
## Development
|
488
492
|
|
489
|
-
To run the tests, you can use RVM and Bundler to create a pristine environment for mysql2 development/hacking.
|
490
493
|
Use 'bundle install' to install the necessary development and testing gems:
|
491
494
|
|
492
495
|
``` sh
|
data/ext/mysql2/client.c
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
+
#include <time.h>
|
3
4
|
#include <errno.h>
|
4
5
|
#ifndef _WIN32
|
6
|
+
#include <sys/types.h>
|
5
7
|
#include <sys/socket.h>
|
6
8
|
#endif
|
7
9
|
#include <unistd.h>
|
10
|
+
#include <fcntl.h>
|
8
11
|
#include "wait_for_single_fd.h"
|
9
12
|
|
10
13
|
#include "mysql_enc_name_to_ruby.h"
|
@@ -12,7 +15,7 @@
|
|
12
15
|
VALUE cMysql2Client;
|
13
16
|
extern VALUE mMysql2, cMysql2Error;
|
14
17
|
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
15
|
-
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql
|
18
|
+
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
|
16
19
|
|
17
20
|
#ifndef HAVE_RB_HASH_DUP
|
18
21
|
static VALUE rb_hash_dup(VALUE other) {
|
@@ -162,31 +165,69 @@ static void *nogvl_connect(void *ptr) {
|
|
162
165
|
return (void *)(client ? Qtrue : Qfalse);
|
163
166
|
}
|
164
167
|
|
165
|
-
static void *nogvl_close(void *ptr) {
|
166
|
-
mysql_client_wrapper *wrapper;
|
167
168
|
#ifndef _WIN32
|
168
|
-
|
169
|
+
/*
|
170
|
+
* Redirect clientfd to a dummy socket for mysql_close to
|
171
|
+
* write, shutdown, and close on as a no-op.
|
172
|
+
* We do this hack because we want to call mysql_close to release
|
173
|
+
* memory, but do not want mysql_close to drop connections in the
|
174
|
+
* parent if the socket got shared in fork.
|
175
|
+
* Returns Qtrue or Qfalse (success or failure)
|
176
|
+
*/
|
177
|
+
static VALUE invalidate_fd(int clientfd)
|
178
|
+
{
|
179
|
+
#ifdef SOCK_CLOEXEC
|
180
|
+
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
181
|
+
int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
182
|
+
if (sockfd < 0) {
|
183
|
+
/* Maybe SOCK_CLOEXEC is defined but not available on this kernel */
|
184
|
+
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
185
|
+
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
186
|
+
}
|
187
|
+
#else
|
188
|
+
/* Well we don't have SOCK_CLOEXEC, so just set FD_CLOEXEC quickly */
|
189
|
+
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
190
|
+
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
169
191
|
#endif
|
192
|
+
|
193
|
+
if (sockfd < 0) {
|
194
|
+
/*
|
195
|
+
* Cannot raise here, because one or both of the following may be true:
|
196
|
+
* a) we have no GVL (in C Ruby)
|
197
|
+
* b) are running as a GC finalizer
|
198
|
+
*/
|
199
|
+
return Qfalse;
|
200
|
+
}
|
201
|
+
|
202
|
+
dup2(sockfd, clientfd);
|
203
|
+
close(sockfd);
|
204
|
+
|
205
|
+
return Qtrue;
|
206
|
+
}
|
207
|
+
#endif /* _WIN32 */
|
208
|
+
|
209
|
+
static void *nogvl_close(void *ptr) {
|
210
|
+
mysql_client_wrapper *wrapper;
|
170
211
|
wrapper = ptr;
|
171
212
|
if (wrapper->connected) {
|
172
213
|
wrapper->active_thread = Qnil;
|
173
214
|
wrapper->connected = 0;
|
174
|
-
/*
|
175
|
-
* we'll send a QUIT message to the server, but that message is more of a
|
176
|
-
* formality than a hard requirement since the socket is getting shutdown
|
177
|
-
* anyways, so ensure the socket write does not block our interpreter
|
178
|
-
*
|
179
|
-
*
|
180
|
-
* if the socket is dead we have no chance of blocking,
|
181
|
-
* so ignore any potential fcntl errors since they don't matter
|
182
|
-
*/
|
183
215
|
#ifndef _WIN32
|
184
|
-
|
185
|
-
|
186
|
-
|
216
|
+
/* Invalidate the socket before calling mysql_close(). This prevents
|
217
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
218
|
+
* the socket. The difference is that invalidate_fd will drop this
|
219
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
220
|
+
* would render the underlying connection unusable, interrupting other
|
221
|
+
* processes which share this object across a fork().
|
222
|
+
*/
|
223
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
224
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
|
225
|
+
close(wrapper->client->net.fd);
|
226
|
+
return NULL;
|
227
|
+
}
|
187
228
|
#endif
|
188
229
|
|
189
|
-
mysql_close(wrapper->client);
|
230
|
+
mysql_close(wrapper->client); /* only used to free memory at this point */
|
190
231
|
}
|
191
232
|
|
192
233
|
return NULL;
|
@@ -215,6 +256,7 @@ static VALUE allocate(VALUE klass) {
|
|
215
256
|
wrapper->active_thread = Qnil;
|
216
257
|
wrapper->server_version = 0;
|
217
258
|
wrapper->reconnect_enabled = 0;
|
259
|
+
wrapper->connect_timeout = 0;
|
218
260
|
wrapper->connected = 0; /* means that a database connection is open */
|
219
261
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
220
262
|
wrapper->refcount = 1;
|
@@ -286,6 +328,8 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
286
328
|
struct nogvl_connect_args args;
|
287
329
|
VALUE rv;
|
288
330
|
GET_CLIENT(self);
|
331
|
+
time_t start_time, end_time;
|
332
|
+
unsigned int elapsed_time, connect_timeout;
|
289
333
|
|
290
334
|
args.host = NIL_P(host) ? NULL : StringValuePtr(host);
|
291
335
|
args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
|
@@ -296,12 +340,31 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
296
340
|
args.mysql = wrapper->client;
|
297
341
|
args.client_flag = NUM2ULONG(flags);
|
298
342
|
|
343
|
+
if (wrapper->connect_timeout)
|
344
|
+
time(&start_time);
|
299
345
|
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
300
346
|
if (rv == Qfalse) {
|
301
|
-
while (rv == Qfalse && errno == EINTR
|
347
|
+
while (rv == Qfalse && errno == EINTR) {
|
348
|
+
if (wrapper->connect_timeout) {
|
349
|
+
time(&end_time);
|
350
|
+
/* avoid long connect timeout from system time changes */
|
351
|
+
if (end_time < start_time)
|
352
|
+
start_time = end_time;
|
353
|
+
elapsed_time = end_time - start_time;
|
354
|
+
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
355
|
+
if (elapsed_time > 0)
|
356
|
+
elapsed_time--;
|
357
|
+
if (elapsed_time >= wrapper->connect_timeout)
|
358
|
+
break;
|
359
|
+
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
360
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
361
|
+
}
|
302
362
|
errno = 0;
|
303
363
|
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
304
364
|
}
|
365
|
+
/* restore the connect timeout for reconnecting */
|
366
|
+
if (wrapper->connect_timeout)
|
367
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
305
368
|
if (rv == Qfalse)
|
306
369
|
return rb_raise_mysql2_error(wrapper);
|
307
370
|
}
|
@@ -449,10 +512,13 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
449
512
|
wrapper->active_thread = Qnil;
|
450
513
|
wrapper->connected = 0;
|
451
514
|
|
452
|
-
/*
|
453
|
-
|
454
|
-
|
455
|
-
wrapper->client->net.fd
|
515
|
+
/* Invalidate the MySQL socket to prevent further communication.
|
516
|
+
* The GC will come along later and call mysql_close to free it.
|
517
|
+
*/
|
518
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
519
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
520
|
+
close(wrapper->client->net.fd);
|
521
|
+
}
|
456
522
|
|
457
523
|
rb_exc_raise(error);
|
458
524
|
|
@@ -737,6 +803,11 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
737
803
|
retval = charval;
|
738
804
|
break;
|
739
805
|
|
806
|
+
case MYSQL_INIT_COMMAND:
|
807
|
+
charval = (const char *)StringValuePtr(value);
|
808
|
+
retval = charval;
|
809
|
+
break;
|
810
|
+
|
740
811
|
default:
|
741
812
|
return Qfalse;
|
742
813
|
}
|
@@ -747,9 +818,15 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
747
818
|
if (result != 0) {
|
748
819
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
749
820
|
} else {
|
750
|
-
/* Special case for
|
751
|
-
|
752
|
-
|
821
|
+
/* Special case for options that are stored in the wrapper struct */
|
822
|
+
switch (opt) {
|
823
|
+
case MYSQL_OPT_RECONNECT:
|
824
|
+
wrapper->reconnect_enabled = boolval;
|
825
|
+
break;
|
826
|
+
case MYSQL_OPT_CONNECT_TIMEOUT:
|
827
|
+
wrapper->connect_timeout = intval;
|
828
|
+
break;
|
829
|
+
}
|
753
830
|
}
|
754
831
|
|
755
832
|
return (result == 0) ? Qtrue : Qfalse;
|
@@ -1121,6 +1198,10 @@ static VALUE set_read_default_group(VALUE self, VALUE value) {
|
|
1121
1198
|
return _mysql_client_options(self, MYSQL_READ_DEFAULT_GROUP, value);
|
1122
1199
|
}
|
1123
1200
|
|
1201
|
+
static VALUE set_init_command(VALUE self, VALUE value) {
|
1202
|
+
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1203
|
+
}
|
1204
|
+
|
1124
1205
|
static VALUE initialize_ext(VALUE self) {
|
1125
1206
|
GET_CLIENT(self);
|
1126
1207
|
|
@@ -1192,6 +1273,7 @@ void init_mysql2_client() {
|
|
1192
1273
|
rb_define_private_method(cMysql2Client, "secure_auth=", set_secure_auth, 1);
|
1193
1274
|
rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
|
1194
1275
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1276
|
+
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1195
1277
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1196
1278
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1197
1279
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
@@ -1208,7 +1290,6 @@ void init_mysql2_client() {
|
|
1208
1290
|
intern_merge_bang = rb_intern("merge!");
|
1209
1291
|
intern_error_number_eql = rb_intern("error_number=");
|
1210
1292
|
intern_sql_state_eql = rb_intern("sql_state=");
|
1211
|
-
intern_server_version = rb_intern("server_version=");
|
1212
1293
|
|
1213
1294
|
#ifdef CLIENT_LONG_PASSWORD
|
1214
1295
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
data/ext/mysql2/client.h
CHANGED
data/ext/mysql2/extconf.rb
CHANGED
@@ -41,8 +41,8 @@ if inc && lib
|
|
41
41
|
@libdir_basename = 'lib'
|
42
42
|
inc, lib = dir_config('mysql')
|
43
43
|
end
|
44
|
-
abort "-----\nCannot find include dir
|
45
|
-
abort "-----\nCannot find library dir
|
44
|
+
abort "-----\nCannot find include dir(s) #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any?{|dir| File.directory?(dir)}
|
45
|
+
abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any?{|dir| File.directory?(dir)}
|
46
46
|
warn "-----\nUsing --with-mysql-dir=#{File.dirname inc}\n-----"
|
47
47
|
rpath_dir = lib
|
48
48
|
elsif mc = (with_config('mysql-config') || Dir[GLOB].first)
|
data/ext/mysql2/infile.c
CHANGED
data/ext/mysql2/mysql2_ext.h
CHANGED
data/lib/mysql2/client.rb
CHANGED
@@ -23,7 +23,10 @@ module Mysql2
|
|
23
23
|
|
24
24
|
initialize_ext
|
25
25
|
|
26
|
-
|
26
|
+
# Set default connect_timeout to avoid unlimited retries from signal interruption
|
27
|
+
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
28
|
+
|
29
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key|
|
27
30
|
next unless opts.key?(key)
|
28
31
|
case key
|
29
32
|
when :reconnect, :local_infile, :secure_auth
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -54,7 +54,7 @@ describe Mysql2::Client do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
client = klient.new :flags => Mysql2::Client::FOUND_ROWS
|
57
|
-
(client.connect_args.last
|
57
|
+
(client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).should be_true
|
58
58
|
end
|
59
59
|
|
60
60
|
it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
|
@@ -66,7 +66,7 @@ describe Mysql2::Client do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
client = klient.new
|
69
|
-
(client.connect_args.last
|
69
|
+
(client.connect_args.last[6] & (Mysql2::Client::REMEMBER_OPTIONS |
|
70
70
|
Mysql2::Client::LONG_PASSWORD |
|
71
71
|
Mysql2::Client::LONG_FLAG |
|
72
72
|
Mysql2::Client::TRANSACTIONS |
|
@@ -74,6 +74,36 @@ describe Mysql2::Client do
|
|
74
74
|
Mysql2::Client::SECURE_CONNECTION)).should be_true
|
75
75
|
end
|
76
76
|
|
77
|
+
it "should execute init command" do
|
78
|
+
options = DatabaseCredentials['root'].dup
|
79
|
+
options[:init_command] = "SET @something = 'setting_value';"
|
80
|
+
client = Mysql2::Client.new(options)
|
81
|
+
result = client.query("SELECT @something;")
|
82
|
+
result.first['@something'].should eq('setting_value')
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should send init_command after reconnect" do
|
86
|
+
options = DatabaseCredentials['root'].dup
|
87
|
+
options[:init_command] = "SET @something = 'setting_value';"
|
88
|
+
options[:reconnect] = true
|
89
|
+
client = Mysql2::Client.new(options)
|
90
|
+
|
91
|
+
result = client.query("SELECT @something;")
|
92
|
+
result.first['@something'].should eq('setting_value')
|
93
|
+
|
94
|
+
# simulate a broken connection
|
95
|
+
begin
|
96
|
+
Timeout.timeout(1, Timeout::Error) do
|
97
|
+
client.query("SELECT sleep(2)")
|
98
|
+
end
|
99
|
+
rescue Timeout::Error
|
100
|
+
end
|
101
|
+
client.ping.should be_false
|
102
|
+
|
103
|
+
result = client.query("SELECT @something;")
|
104
|
+
result.first['@something'].should eq('setting_value')
|
105
|
+
end
|
106
|
+
|
77
107
|
it "should have a global default_query_options hash" do
|
78
108
|
Mysql2::Client.should respond_to(:default_query_options)
|
79
109
|
end
|
@@ -126,6 +156,27 @@ describe Mysql2::Client do
|
|
126
156
|
final_count.should == before_count
|
127
157
|
end
|
128
158
|
|
159
|
+
if Process.respond_to?(:fork)
|
160
|
+
it "should not close connections when running in a child process" do
|
161
|
+
GC.start
|
162
|
+
sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
|
163
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
164
|
+
|
165
|
+
fork do
|
166
|
+
client.query('SELECT 1')
|
167
|
+
client = nil
|
168
|
+
GC.start
|
169
|
+
sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
|
170
|
+
end
|
171
|
+
|
172
|
+
Process.wait
|
173
|
+
|
174
|
+
# this will throw an error if the underlying socket was shutdown by the
|
175
|
+
# child's GC
|
176
|
+
expect { client.query('SELECT 1') }.to_not raise_exception
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
129
180
|
it "should be able to connect to database with numeric-only name" do
|
130
181
|
lambda {
|
131
182
|
creds = DatabaseCredentials['numericuser']
|
@@ -397,9 +448,8 @@ describe Mysql2::Client do
|
|
397
448
|
end
|
398
449
|
|
399
450
|
it "should close the connection when an exception is raised" do
|
400
|
-
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
401
451
|
begin
|
402
|
-
Timeout.timeout(1) do
|
452
|
+
Timeout.timeout(1, Timeout::Error) do
|
403
453
|
@client.query("SELECT sleep(2)")
|
404
454
|
end
|
405
455
|
rescue Timeout::Error
|
@@ -411,10 +461,9 @@ describe Mysql2::Client do
|
|
411
461
|
end
|
412
462
|
|
413
463
|
it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
|
414
|
-
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
415
464
|
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
|
416
465
|
begin
|
417
|
-
Timeout.timeout(1) do
|
466
|
+
Timeout.timeout(1, Timeout::Error) do
|
418
467
|
client.query("SELECT sleep(2)")
|
419
468
|
end
|
420
469
|
rescue Timeout::Error
|
@@ -426,10 +475,9 @@ describe Mysql2::Client do
|
|
426
475
|
end
|
427
476
|
|
428
477
|
it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
|
429
|
-
pending "Ruby 2.1 has changed Timeout behavior." if RUBY_VERSION =~ /2.1/
|
430
478
|
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
431
479
|
begin
|
432
|
-
Timeout.timeout(1) do
|
480
|
+
Timeout.timeout(1, Timeout::Error) do
|
433
481
|
client.query("SELECT sleep(2)")
|
434
482
|
end
|
435
483
|
rescue Timeout::Error
|
@@ -442,7 +490,7 @@ describe Mysql2::Client do
|
|
442
490
|
client.reconnect = true
|
443
491
|
|
444
492
|
begin
|
445
|
-
Timeout.timeout(1) do
|
493
|
+
Timeout.timeout(1, Timeout::Error) do
|
446
494
|
client.query("SELECT sleep(2)")
|
447
495
|
end
|
448
496
|
rescue Timeout::Error
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Lopez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -128,7 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
128
|
version: '0'
|
129
129
|
requirements: []
|
130
130
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.0.
|
131
|
+
rubygems_version: 2.0.14
|
132
132
|
signing_key:
|
133
133
|
specification_version: 4
|
134
134
|
summary: A simple, fast Mysql library for Ruby, binding to libmysql
|