mysql2 0.2.22 → 0.2.23

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fcca31d92606bbe214af44d546de724f590a1136
4
- data.tar.gz: c927057bf2398485dee1bb4a705972f8d635ccf0
3
+ metadata.gz: 4fcf19fa554da3669deb070a7089eac9c4b1dc22
4
+ data.tar.gz: c8616ad6f03b717803b3dbe57a4d51008a62d770
5
5
  SHA512:
6
- metadata.gz: 8de2036b42828457b7fa485dfbdeec79dd3354c5ff32f81a08f6bdfaacbc51ae80d1549c98d821f8e35aec339f0769a6af6708c75d38614e3c5b1c92e32140f3
7
- data.tar.gz: 4d8bd2f85a3b5b3d30e1f1062ed6e351df444e8adbe9fc9e0758854be870580caf1942b4f0923b08b18e6c27e9a7043cd803fb8353aa3d72174e0d8a40f9dbfd
6
+ metadata.gz: eb991cb7b45721f0904cdaf1fe996ab8d0c3cf7e12369e582c26af2076fdfd819c5e7e9db6bd2de711577f8be332de731fe94ddf3e1bd9e66bbfc410bf82acd1
7
+ data.tar.gz: 29dd571c4d28ed12fb2bcedff13862b4610e80ec785f28cda72fbe2a68976f74d67c6ef10c7bec9a8393dd6fb8510f3cafa368a8bfbc2d5f248d5e9c4dbe64e4
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
@@ -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"),
@@ -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)
@@ -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
@@ -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.2.22"
2
+ VERSION = "0.2.23"
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.2.22
4
+ version: 0.2.23
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
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  version: '0'
131
131
  requirements: []
132
132
  rubyforge_project:
133
- rubygems_version: 2.0.3
133
+ rubygems_version: 2.0.14
134
134
  signing_key:
135
135
  specification_version: 4
136
136
  summary: A simple, fast Mysql library for Ruby, binding to libmysql