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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1895b2fe6d621bc0608c1c16c7dde5872aff9af9
4
- data.tar.gz: 4757365df13127c09c35c3b2cbf84ea35c9ab872
3
+ metadata.gz: 24297b49b4e7641f01f0b35438a160e8667c6b3b
4
+ data.tar.gz: ccea57bd6d82f744ebb04420791297bcb7a0bfa9
5
5
  SHA512:
6
- metadata.gz: 9feaf2446a8f5c72594fb3958d2675b94b64849f1a8aa0054a7d2fd31191ac82566ae5d008d7e3cc84af329bf009461010e3e8617919abefeede90567ea6f571
7
- data.tar.gz: 114eb344643a6ec32cf8624e268551334776eb0196105888dfff3a21f42b4889123a00a32f5692015c1ab1638b02b565c7a20a14a6df3d8371f471d9ad0ba702
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
- # NOTE: the :symbolize_keys and future options will likely move to the #query method soon
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
- ## Active Record
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
- To use the Active Record driver (with or without rails), all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
396
- That was easy right? :)
432
+ ### Active Record
397
433
 
398
- NOTE: as of 0.3.0, and Active Record 3.1 - the Active Record adapter has been pulled out of this gem and into Active Record itself. If you need to use mysql2 with
399
- Rails versions < 3.1 make sure and specify `gem "mysql2", "~> 0.2.7"` in your Gemfile
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
- ## Asynchronous Active Record
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
- ## Sequel
441
+ ### Sequel
406
442
 
407
- The Sequel adapter was pulled out into Sequel core (will be part of the next release) and can be used by specifying the "mysql2://" prefix to your connection specification.
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
- ## EventMachine
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
- ## Lazy Everything
469
+ ## Benchmarks and Comparison
433
470
 
434
- Well... almost ;)
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
- ## Compatibility
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
- Me: It sure does, but it only hands you nil and strings for field values. Leaving you to convert
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
- Someone: OK fine, but do_mysql can already give me back values with Ruby objects mapped to MySQL types.
467
-
468
- Me: Yep, but it's API is considerably more complex *and* can be ~2x slower.
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
- user system total real
479
- Mysql2
480
- 0.750000 0.180000 0.930000 ( 1.821655)
481
- do_mysql
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, intern_server_version;
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
- int flags;
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
- flags = fcntl(wrapper->client->net.fd, F_GETFL);
185
- if (flags > 0 && !(flags & O_NONBLOCK))
186
- fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
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 && !mysql_errno(wrapper->client)) {
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
- /* manually close the socket for read/write
453
- this feels dirty, but is there another way? */
454
- close(wrapper->client->net.fd);
455
- wrapper->client->net.fd = -1;
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 reconnect, this option is also stored in the wrapper struct */
751
- if (opt == MYSQL_OPT_RECONNECT)
752
- wrapper->reconnect_enabled = boolval;
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
@@ -41,6 +41,7 @@ typedef struct {
41
41
  VALUE active_thread; /* rb_thread_current() or Qnil */
42
42
  long server_version;
43
43
  int reconnect_enabled;
44
+ int connect_timeout;
44
45
  int active;
45
46
  int connected;
46
47
  int initialized;
@@ -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 at #{inc}\n-----" unless inc && File.directory?(inc)
45
- abort "-----\nCannot find library dir at #{lib}\n-----" unless lib && File.directory?(lib)
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
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <errno.h>
4
4
  #include <unistd.h>
5
+ #include <fcntl.h>
5
6
 
6
7
  #define ERROR_LEN 1024
7
8
  typedef struct
@@ -6,7 +6,6 @@
6
6
  we'll never modify the pointers we get back from RSTRING_PTR */
7
7
  #define RSTRING_NOT_MODIFIED
8
8
  #include <ruby.h>
9
- #include <fcntl.h>
10
9
 
11
10
  #ifndef HAVE_UINT
12
11
  #define HAVE_UINT
data/lib/mysql2/client.rb CHANGED
@@ -23,7 +23,10 @@ module Mysql2
23
23
 
24
24
  initialize_ext
25
25
 
26
- [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth].each do |key|
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
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.3.15"
2
+ VERSION = "0.3.16"
3
3
  end
@@ -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.last & Mysql2::Client::FOUND_ROWS).should be_true
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.last & (Mysql2::Client::REMEMBER_OPTIONS |
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.15
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-01-23 00:00:00.000000000 Z
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.3
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