mysql2 0.3.15 → 0.3.16
Sign up to get free protection for your applications and to get access to all the features.
- 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
|