mysql2 0.4.4 → 0.4.10

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: 03a4d85f4672425142825728e8c69d84f4f898af
4
- data.tar.gz: 3c78c54532a405a2e05b85692dd18f219729c46c
3
+ metadata.gz: 75b3d925930b92cf7b1a36fa196d334e245919ac
4
+ data.tar.gz: 2bbe0a78b156f8c5b59643c4d57a7ce19b764bcc
5
5
  SHA512:
6
- metadata.gz: 94cc9c40b48e34c8831315eff9d80ae043569d5a69708ca157f976b9cba75a7c7672a3f12300a3312a02a24b87a1b81185216792fd1fe9968f92446c6f33eed5
7
- data.tar.gz: 2092fd9e8cb83c053bd2b0b89e09fa1f872a535de01dd2f1927bcd3eaced860764ff3e8793d1d971cc9eeb08a4d504a3d3c4da7cf7ab67fa21f0bcc84bc21a3c
6
+ metadata.gz: 602f336b5ed83421862b9dec36a9ddbd477dcbacc3ef16d58d5252072dba0bc7f7a955000482414eda1d104bda72ded87f3f4c795f8b4b4d36999bc6ee171e4b
7
+ data.tar.gz: 20281fda66cf4595edc05ac6a933d5f641c2f9f87771e8ace1e9de00902ecea54ddbc2d1b743c3dbd97b48c795d0ad32f9ab785e5848a4f3de92c7ddebeef659
data/README.md CHANGED
@@ -85,6 +85,9 @@ You may use MacPorts, Homebrew, or a native MySQL installer package. The most
85
85
  common paths will be automatically searched. If you want to select a specific
86
86
  MySQL directory, use the `--with-mysql-dir` or `--with-mysql-config` options above.
87
87
 
88
+ If you have not done so already, you will need to install the XCode select tools by running
89
+ `xcode-select --install`.
90
+
88
91
  ### Windows
89
92
  Make sure that you have Ruby and the DevKit compilers installed. We recommend
90
93
  the [Ruby Installer](http://rubyinstaller.org) distribution.
@@ -109,7 +112,7 @@ Connect to a database:
109
112
  ``` ruby
110
113
  # this takes a hash of options, almost all of which map directly
111
114
  # to the familiar database.yml in rails
112
- # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html
115
+ # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html
113
116
  client = Mysql2::Client.new(:host => "localhost", :username => "root")
114
117
  ```
115
118
 
@@ -164,8 +167,8 @@ by the query like this:
164
167
  ``` ruby
165
168
  headers = results.fields # <= that's an array of field names, in order
166
169
  results.each(:as => :array) do |row|
167
- # Each row is an array, ordered the same as the query results
168
- # An otter's den is called a "holt" or "couch"
170
+ # Each row is an array, ordered the same as the query results
171
+ # An otter's den is called a "holt" or "couch"
169
172
  end
170
173
  ```
171
174
 
@@ -203,12 +206,25 @@ Mysql2::Client.new(
203
206
  :reconnect = true/false,
204
207
  :local_infile = true/false,
205
208
  :secure_auth = true/false,
209
+ :ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
206
210
  :default_file = '/path/to/my.cfg',
207
211
  :default_group = 'my.cfg section',
208
212
  :init_command => sql
209
213
  )
210
214
  ```
211
215
 
216
+ ### Connecting to MySQL on localhost and elsewhere
217
+
218
+ The underlying MySQL client library uses the `:host` parameter to determine the
219
+ type of connection to make, with special interpretation you should be aware of:
220
+
221
+ * An empty value or `"localhost"` will attempt a local connection:
222
+ * On Unix, connect to the default local socket path. (To set a custom socket
223
+ path, use the `:socket` parameter).
224
+ * On Windows, connect using a shared-memory connection, if enabled, or TCP.
225
+ * A value of `"."` on Windows specifies a named-pipe connection.
226
+ * An IPv4 or IPv6 address will result in a TCP connection.
227
+ * Any other value will be looked up as a hostname for a TCP connection.
212
228
 
213
229
  ### SSL options
214
230
 
@@ -231,47 +247,6 @@ Mysql2::Client.new(
231
247
  )
232
248
  ```
233
249
 
234
- ### Multiple result sets
235
-
236
- You can also retrieve multiple result sets. For this to work you need to
237
- connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
238
- be used with stored procedures that return more than one result set, and for
239
- bundling several SQL statements into a single call to `client.query`.
240
-
241
- ``` ruby
242
- client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
243
- result = client.query('CALL sp_customer_list( 25, 10 )')
244
- # result now contains the first result set
245
- while client.next_result
246
- result = client.store_result
247
- # result now contains the next result set
248
- end
249
- ```
250
-
251
- Repeated calls to `client.next_result` will return true, false, or raise an
252
- exception if the respective query erred. When `client.next_result` returns true,
253
- call `client.store_result` to retrieve a result object. Exceptions are not
254
- raised until `client.next_result` is called to find the status of the respective
255
- query. Subsequent queries are not executed if an earlier query raised an
256
- exception. Subsequent calls to `client.next_result` will return false.
257
-
258
- ``` ruby
259
- result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
260
- p result.first
261
-
262
- while client.next_result
263
- result = client.store_result
264
- p result.first
265
- end
266
- ```
267
-
268
- Yields:
269
- ```
270
- {"1"=>1}
271
- {"2"=>2}
272
- next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
273
- ```
274
-
275
250
  ### Secure auth
276
251
 
277
252
  Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
@@ -328,6 +303,47 @@ It is useful if you want to provide session options which survive reconnection.
328
303
  Mysql2::Client.new(:init_command => "SET @@SESSION.sql_mode = 'STRICT_ALL_TABLES'")
329
304
  ```
330
305
 
306
+ ### Multiple result sets
307
+
308
+ You can also retrieve multiple result sets. For this to work you need to
309
+ connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
310
+ be used with stored procedures that return more than one result set, and for
311
+ bundling several SQL statements into a single call to `client.query`.
312
+
313
+ ``` ruby
314
+ client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
315
+ result = client.query('CALL sp_customer_list( 25, 10 )')
316
+ # result now contains the first result set
317
+ while client.next_result
318
+ result = client.store_result
319
+ # result now contains the next result set
320
+ end
321
+ ```
322
+
323
+ Repeated calls to `client.next_result` will return true, false, or raise an
324
+ exception if the respective query erred. When `client.next_result` returns true,
325
+ call `client.store_result` to retrieve a result object. Exceptions are not
326
+ raised until `client.next_result` is called to find the status of the respective
327
+ query. Subsequent queries are not executed if an earlier query raised an
328
+ exception. Subsequent calls to `client.next_result` will return false.
329
+
330
+ ``` ruby
331
+ result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
332
+ p result.first
333
+
334
+ while client.next_result
335
+ result = client.store_result
336
+ p result.first
337
+ end
338
+ ```
339
+
340
+ Yields:
341
+ ```
342
+ {"1"=>1}
343
+ {"2"=>2}
344
+ next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
345
+ ```
346
+
331
347
  ## Cascading config
332
348
 
333
349
  The default config hash is at:
@@ -398,6 +414,15 @@ client = Mysql2::Client.new
398
414
  result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
399
415
  ```
400
416
 
417
+ Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
418
+
419
+ ``` ruby
420
+ client = Mysql2::Client.new
421
+ result = client.query("SELECT true", :cast_booleans => true)
422
+ ```
423
+
424
+ CAST function wouldn't help here as there's no way to cast to TINYINT(1). Apparently the only way to solve this is to use a stored procedure with return type set to TINYINT(1).
425
+
401
426
  ### Skipping casting
402
427
 
403
428
  Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false. (Note that :cast => false overrides :cast_booleans => true.)
@@ -484,13 +509,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
484
509
 
485
510
  This gem is tested with the following Ruby versions on Linux and Mac OS X:
486
511
 
487
- * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x
512
+ * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
488
513
  * Ruby Enterprise Edition (based on MRI 1.8.7)
489
- * Rubinius 2.x, 3.x
514
+ * Rubinius 2.x and 3.x do work but may fail under some workloads
490
515
 
491
516
  This gem is tested with the following MySQL and MariaDB versions:
492
517
 
493
- * MySQL 5.5, 5.6, 5.7
518
+ * MySQL 5.5, 5.6, 5.7, 8.0
494
519
  * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
495
520
  * MariaDB 5.5, 10.0, 10.1
496
521
 
data/ext/mysql2/client.c CHANGED
@@ -30,9 +30,21 @@ VALUE rb_hash_dup(VALUE other) {
30
30
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
31
  }
32
32
 
33
+ #if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
34
+ #define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
35
+ #elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
36
+ #define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
37
+ #endif
38
+
39
+ #define REQUIRE_CONNECTED(wrapper) \
40
+ REQUIRE_INITIALIZED(wrapper) \
41
+ if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
42
+ rb_raise(cMysql2Error, "MySQL client is not connected"); \
43
+ }
44
+
33
45
  #define REQUIRE_NOT_CONNECTED(wrapper) \
34
46
  REQUIRE_INITIALIZED(wrapper) \
35
- if (wrapper->connected) { \
47
+ if (CONNECTED(wrapper)) { \
36
48
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
37
49
  }
38
50
 
@@ -41,12 +53,24 @@ VALUE rb_hash_dup(VALUE other) {
41
53
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
42
54
  * linking against the server itself
43
55
  */
44
- #ifdef LIBMYSQL_VERSION
56
+ #if defined(MARIADB_CLIENT_VERSION_STR)
57
+ #define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
58
+ #elif defined(LIBMYSQL_VERSION)
45
59
  #define MYSQL_LINK_VERSION LIBMYSQL_VERSION
46
60
  #else
47
61
  #define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
48
62
  #endif
49
63
 
64
+ /*
65
+ * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
66
+ */
67
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
68
+ #define SSL_MODE_DISABLED 1
69
+ #define SSL_MODE_REQUIRED 3
70
+ #define HAVE_CONST_SSL_MODE_DISABLED
71
+ #define HAVE_CONST_SSL_MODE_REQUIRED
72
+ #endif
73
+
50
74
  /*
51
75
  * used to pass all arguments to mysql_real_connect while inside
52
76
  * rb_thread_call_without_gvl
@@ -83,6 +107,42 @@ struct nogvl_select_db_args {
83
107
  char *db;
84
108
  };
85
109
 
110
+ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
111
+ unsigned long version = mysql_get_client_version();
112
+
113
+ if (version < 50703) {
114
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
115
+ return Qnil;
116
+ }
117
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
118
+ GET_CLIENT(self);
119
+ int val = NUM2INT( setting );
120
+ if (version >= 50703 && version < 50711) {
121
+ if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
122
+ bool b = ( val == SSL_MODE_REQUIRED );
123
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
124
+ return INT2NUM(result);
125
+ } else {
126
+ rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
127
+ return Qnil;
128
+ }
129
+ }
130
+ #endif
131
+ #ifdef FULL_SSL_MODE_SUPPORT
132
+ GET_CLIENT(self);
133
+ int val = NUM2INT( setting );
134
+
135
+ if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
136
+ rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
137
+ }
138
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
139
+
140
+ return INT2NUM(result);
141
+ #endif
142
+ #ifdef NO_SSL_MODE_SUPPORT
143
+ return Qnil;
144
+ #endif
145
+ }
86
146
  /*
87
147
  * non-blocking mysql_*() functions that we won't be wrapping since
88
148
  * they do not appear to hit the network nor issue any interruptible
@@ -211,11 +271,10 @@ static VALUE invalidate_fd(int clientfd)
211
271
  static void *nogvl_close(void *ptr) {
212
272
  mysql_client_wrapper *wrapper = ptr;
213
273
 
214
- if (wrapper->client) {
274
+ if (!wrapper->closed) {
215
275
  mysql_close(wrapper->client);
216
- xfree(wrapper->client);
217
- wrapper->client = NULL;
218
- wrapper->connected = 0;
276
+ wrapper->closed = 1;
277
+ wrapper->reconnect_enabled = 0;
219
278
  wrapper->active_thread = Qnil;
220
279
  }
221
280
 
@@ -234,7 +293,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
234
293
 
235
294
  if (wrapper->refcount == 0) {
236
295
  #ifndef _WIN32
237
- if (wrapper->connected && !wrapper->automatic_close) {
296
+ if (CONNECTED(wrapper) && !wrapper->automatic_close) {
238
297
  /* The client is being garbage collected while connected. Prevent
239
298
  * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
240
299
  * the socket by invalidating it. invalidate_fd() will drop this
@@ -246,10 +305,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
246
305
  fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
247
306
  close(wrapper->client->net.fd);
248
307
  }
308
+ wrapper->client->net.fd = -1;
249
309
  }
250
310
  #endif
251
311
 
252
312
  nogvl_close(wrapper);
313
+ xfree(wrapper->client);
253
314
  xfree(wrapper);
254
315
  }
255
316
  }
@@ -264,9 +325,9 @@ static VALUE allocate(VALUE klass) {
264
325
  wrapper->server_version = 0;
265
326
  wrapper->reconnect_enabled = 0;
266
327
  wrapper->connect_timeout = 0;
267
- wrapper->connected = 0; /* means that a database connection is open */
268
328
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
269
329
  wrapper->refcount = 1;
330
+ wrapper->closed = 0;
270
331
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
271
332
 
272
333
  return obj;
@@ -397,7 +458,6 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
397
458
  }
398
459
 
399
460
  wrapper->server_version = mysql_get_server_version(wrapper->client);
400
- wrapper->connected = 1;
401
461
  return self;
402
462
  }
403
463
 
@@ -412,13 +472,23 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
412
472
  static VALUE rb_mysql_client_close(VALUE self) {
413
473
  GET_CLIENT(self);
414
474
 
415
- if (wrapper->connected) {
475
+ if (wrapper->client) {
416
476
  rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
417
477
  }
418
478
 
419
479
  return Qnil;
420
480
  }
421
481
 
482
+ /* call-seq:
483
+ * client.closed?
484
+ *
485
+ * @return [Boolean]
486
+ */
487
+ static VALUE rb_mysql_client_closed(VALUE self) {
488
+ GET_CLIENT(self);
489
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
490
+ }
491
+
422
492
  /*
423
493
  * mysql_send_query is unlikely to block since most queries are small
424
494
  * enough to fit in a socket buffer, but sometimes large UPDATE and
@@ -451,7 +521,7 @@ static VALUE do_send_query(void *args) {
451
521
  */
452
522
  static void *nogvl_read_query_result(void *ptr) {
453
523
  MYSQL * client = ptr;
454
- my_bool res = mysql_read_query_result(client);
524
+ bool res = mysql_read_query_result(client);
455
525
 
456
526
  return (void *)(res == 0 ? Qtrue : Qfalse);
457
527
  }
@@ -538,14 +608,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
538
608
  GET_CLIENT(self);
539
609
 
540
610
  wrapper->active_thread = Qnil;
541
- wrapper->connected = 0;
542
611
 
543
612
  /* Invalidate the MySQL socket to prevent further communication.
544
613
  * The GC will come along later and call mysql_close to free it.
545
614
  */
546
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
547
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
548
- close(wrapper->client->net.fd);
615
+ if (CONNECTED(wrapper)) {
616
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
617
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
618
+ close(wrapper->client->net.fd);
619
+ }
620
+ wrapper->client->net.fd = -1;
549
621
  }
550
622
 
551
623
  rb_exc_raise(error);
@@ -594,26 +666,32 @@ static VALUE do_query(void *args) {
594
666
 
595
667
  return Qnil;
596
668
  }
597
- #else
598
- static VALUE finish_and_mark_inactive(void *args) {
599
- VALUE self = args;
600
- MYSQL_RES *result;
669
+ #endif
601
670
 
671
+ static VALUE disconnect_and_mark_inactive(VALUE self) {
602
672
  GET_CLIENT(self);
603
673
 
674
+ /* Check if execution terminated while result was still being read. */
604
675
  if (!NIL_P(wrapper->active_thread)) {
605
- /* if we got here, the result hasn't been read off the wire yet
606
- so lets do that and then throw it away because we have no way
607
- of getting it back up to the caller from here */
608
- result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
609
- mysql_free_result(result);
610
-
676
+ if (CONNECTED(wrapper)) {
677
+ /* Invalidate the MySQL socket to prevent further communication. */
678
+ #ifndef _WIN32
679
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
680
+ rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
681
+ close(wrapper->client->net.fd);
682
+ }
683
+ #else
684
+ close(wrapper->client->net.fd);
685
+ #endif
686
+ wrapper->client->net.fd = -1;
687
+ }
688
+ /* Skip mysql client check performed before command execution. */
689
+ wrapper->client->status = MYSQL_STATUS_READY;
611
690
  wrapper->active_thread = Qnil;
612
691
  }
613
692
 
614
693
  return Qnil;
615
694
  }
616
- #endif
617
695
 
618
696
  void rb_mysql_client_set_active_thread(VALUE self) {
619
697
  VALUE thread_current = rb_thread_current();
@@ -707,13 +785,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
707
785
 
708
786
  rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
709
787
 
710
- return rb_mysql_client_async_result(self);
788
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
711
789
  }
712
790
  #else
713
791
  do_send_query(&args);
714
792
 
715
793
  /* this will just block until the result is ready */
716
- return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
794
+ return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
717
795
  #endif
718
796
  }
719
797
 
@@ -772,7 +850,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
772
850
  const void *retval = NULL;
773
851
  unsigned int intval = 0;
774
852
  const char * charval = NULL;
775
- my_bool boolval;
853
+ bool boolval;
776
854
 
777
855
  GET_CLIENT(self);
778
856
 
@@ -807,10 +885,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
807
885
  retval = &boolval;
808
886
  break;
809
887
 
888
+ #ifdef MYSQL_SECURE_AUTH
810
889
  case MYSQL_SECURE_AUTH:
811
890
  boolval = (value == Qfalse ? 0 : 1);
812
891
  retval = &boolval;
813
892
  break;
893
+ #endif
814
894
 
815
895
  case MYSQL_READ_DEFAULT_FILE:
816
896
  charval = (const char *)StringValueCStr(value);
@@ -827,6 +907,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
827
907
  retval = charval;
828
908
  break;
829
909
 
910
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
911
+ case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
912
+ boolval = (value == Qfalse ? 0 : 1);
913
+ retval = &boolval;
914
+ break;
915
+ #endif
916
+
830
917
  default:
831
918
  return Qfalse;
832
919
  }
@@ -1016,7 +1103,7 @@ static void *nogvl_ping(void *ptr) {
1016
1103
  static VALUE rb_mysql_client_ping(VALUE self) {
1017
1104
  GET_CLIENT(self);
1018
1105
 
1019
- if (!wrapper->connected) {
1106
+ if (!CONNECTED(wrapper)) {
1020
1107
  return Qfalse;
1021
1108
  } else {
1022
1109
  return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
@@ -1192,6 +1279,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1192
1279
  #endif
1193
1280
  GET_CLIENT(self);
1194
1281
 
1282
+ Check_Type(value, T_STRING);
1195
1283
  charset_name = RSTRING_PTR(value);
1196
1284
 
1197
1285
  #ifdef HAVE_RUBY_ENCODING_H
@@ -1228,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1228
1316
  }
1229
1317
 
1230
1318
  static VALUE set_secure_auth(VALUE self, VALUE value) {
1319
+ /* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
1320
+ #ifdef MYSQL_SECURE_AUTH
1231
1321
  return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
1322
+ #else
1323
+ return Qfalse;
1324
+ #endif
1232
1325
  }
1233
1326
 
1234
1327
  static VALUE set_read_default_file(VALUE self, VALUE value) {
@@ -1243,6 +1336,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1243
1336
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1244
1337
  }
1245
1338
 
1339
+ static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
+ #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
+ return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
1342
+ #else
1343
+ rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
1344
+ #endif
1345
+ }
1346
+
1246
1347
  static VALUE initialize_ext(VALUE self) {
1247
1348
  GET_CLIENT(self);
1248
1349
 
@@ -1303,6 +1404,7 @@ void init_mysql2_client() {
1303
1404
  rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1304
1405
 
1305
1406
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1407
+ rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
1306
1408
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1307
1409
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1308
1410
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
@@ -1337,6 +1439,8 @@ void init_mysql2_client() {
1337
1439
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1338
1440
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1339
1441
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1442
+ rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1443
+ rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1340
1444
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1341
1445
  rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1342
1446
  rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
@@ -1358,6 +1462,10 @@ void init_mysql2_client() {
1358
1462
  #ifdef CLIENT_LONG_PASSWORD
1359
1463
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
1360
1464
  LONG2NUM(CLIENT_LONG_PASSWORD));
1465
+ #else
1466
+ /* HACK because MariaDB 10.2 no longer defines this constant,
1467
+ * but we're using it in our default connection flags. */
1468
+ rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
1361
1469
  #endif
1362
1470
 
1363
1471
  #ifdef CLIENT_FOUND_ROWS
@@ -1464,4 +1572,31 @@ void init_mysql2_client() {
1464
1572
  rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
1465
1573
  LONG2NUM(CLIENT_BASIC_FLAGS));
1466
1574
  #endif
1575
+
1576
+ #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1577
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1578
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1579
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1580
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1581
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1582
+ #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1583
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1584
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1585
+ #endif
1586
+
1587
+ #ifndef HAVE_CONST_SSL_MODE_DISABLED
1588
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
1589
+ #endif
1590
+ #ifndef HAVE_CONST_SSL_MODE_PREFERRED
1591
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
1592
+ #endif
1593
+ #ifndef HAVE_CONST_SSL_MODE_REQUIRED
1594
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
1595
+ #endif
1596
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
1597
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
1598
+ #endif
1599
+ #ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
1600
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1601
+ #endif
1467
1602
  }
data/ext/mysql2/client.h CHANGED
@@ -44,19 +44,12 @@ typedef struct {
44
44
  unsigned int connect_timeout;
45
45
  int active;
46
46
  int automatic_close;
47
- int connected;
48
47
  int initialized;
49
48
  int refcount;
50
- int freed;
49
+ int closed;
51
50
  MYSQL *client;
52
51
  } mysql_client_wrapper;
53
52
 
54
- #define REQUIRE_CONNECTED(wrapper) \
55
- REQUIRE_INITIALIZED(wrapper) \
56
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
57
- rb_raise(cMysql2Error, "closed MySQL connection"); \
58
- }
59
-
60
53
  void rb_mysql_client_set_active_thread(VALUE self);
61
54
 
62
55
  #define GET_CLIENT(self) \
@@ -12,6 +12,20 @@ def asplode(lib)
12
12
  end
13
13
  end
14
14
 
15
+ def add_ssl_defines(header)
16
+ all_modes_found = %w(SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY).inject(true) do |m, ssl_mode|
17
+ m && have_const(ssl_mode, header)
18
+ end
19
+ $CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
20
+ # if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
21
+ has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
22
+ $CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
23
+ end
24
+
25
+ # 2.1+
26
+ have_func('rb_absint_size')
27
+ have_func('rb_absint_singlebit_p')
28
+
15
29
  # 2.0-only
16
30
  have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
17
31
 
@@ -20,6 +34,7 @@ have_func('rb_thread_blocking_region')
20
34
  have_func('rb_wait_for_single_fd')
21
35
  have_func('rb_hash_dup')
22
36
  have_func('rb_intern3')
37
+ have_func('rb_big_cmp')
23
38
 
24
39
  # borrowed from mysqlplus
25
40
  # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
@@ -37,6 +52,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
37
52
  /usr/local/opt/mysql5*
38
53
  ).map { |dir| dir << '/bin' }
39
54
 
55
+ # For those without HOMEBREW_ROOT in PATH
56
+ dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
57
+
40
58
  GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
41
59
 
42
60
  # If the user has provided a --with-mysql-dir argument, we must respect it or fail.
@@ -87,11 +105,17 @@ else
87
105
  asplode 'mysql.h'
88
106
  end
89
107
 
90
- %w(errmsg.h mysqld_error.h).each do |h|
91
- header = [prefix, h].compact.join '/'
108
+ %w(errmsg.h).each do |h|
109
+ header = [prefix, h].compact.join('/')
92
110
  asplode h unless have_header header
93
111
  end
94
112
 
113
+ mysql_h = [prefix, 'mysql.h'].compact.join('/')
114
+ add_ssl_defines(mysql_h)
115
+ have_struct_member('MYSQL', 'net.vio', mysql_h)
116
+ have_struct_member('MYSQL', 'net.pvio', mysql_h)
117
+ have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
118
+
95
119
  # This is our wishlist. We use whichever flags work on the host.
96
120
  # -Wall and -Wextra are included by default.
97
121
  wishlist = [
@@ -11,14 +11,10 @@ void Init_mysql2(void);
11
11
 
12
12
  #ifdef HAVE_MYSQL_H
13
13
  #include <mysql.h>
14
- #include <mysql_com.h>
15
14
  #include <errmsg.h>
16
- #include <mysqld_error.h>
17
15
  #else
18
16
  #include <mysql/mysql.h>
19
- #include <mysql/mysql_com.h>
20
17
  #include <mysql/errmsg.h>
21
- #include <mysql/mysqld_error.h>
22
18
  #endif
23
19
 
24
20
  #ifdef HAVE_RUBY_ENCODING_H