mysql2 0.4.2 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +77 -51
- data/ext/mysql2/client.c +242 -51
- data/ext/mysql2/client.h +2 -12
- data/ext/mysql2/extconf.rb +26 -2
- data/ext/mysql2/mysql2_ext.h +0 -4
- data/ext/mysql2/result.c +40 -22
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +133 -32
- data/lib/mysql2/client.rb +19 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +1 -0
- data/spec/mysql2/client_spec.rb +211 -111
- data/spec/mysql2/error_spec.rb +4 -6
- data/spec/mysql2/result_spec.rb +71 -36
- data/spec/mysql2/statement_spec.rb +144 -52
- data/spec/spec_helper.rb +73 -59
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- metadata +12 -11
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
|
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
|
-
#
|
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->
|
274
|
+
if (!wrapper->closed) {
|
215
275
|
mysql_close(wrapper->client);
|
216
|
-
|
217
|
-
wrapper->
|
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->
|
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
|
}
|
@@ -259,13 +320,14 @@ static VALUE allocate(VALUE klass) {
|
|
259
320
|
mysql_client_wrapper * wrapper;
|
260
321
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
261
322
|
wrapper->encoding = Qnil;
|
262
|
-
|
323
|
+
wrapper->active_thread = Qnil;
|
324
|
+
wrapper->automatic_close = 1;
|
263
325
|
wrapper->server_version = 0;
|
264
326
|
wrapper->reconnect_enabled = 0;
|
265
327
|
wrapper->connect_timeout = 0;
|
266
|
-
wrapper->connected = 0; /* means that a database connection is open */
|
267
328
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
268
329
|
wrapper->refcount = 1;
|
330
|
+
wrapper->closed = 0;
|
269
331
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
270
332
|
|
271
333
|
return obj;
|
@@ -331,6 +393,26 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
331
393
|
return rb_str;
|
332
394
|
}
|
333
395
|
|
396
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
397
|
+
{
|
398
|
+
const char *cipher;
|
399
|
+
VALUE rb_str;
|
400
|
+
GET_CLIENT(self);
|
401
|
+
|
402
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
403
|
+
|
404
|
+
if (cipher == NULL) {
|
405
|
+
return Qnil;
|
406
|
+
}
|
407
|
+
|
408
|
+
rb_str = rb_str_new2(cipher);
|
409
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
410
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
411
|
+
#endif
|
412
|
+
|
413
|
+
return rb_str;
|
414
|
+
}
|
415
|
+
|
334
416
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
335
417
|
struct nogvl_connect_args args;
|
336
418
|
time_t start_time, end_time, elapsed_time, connect_timeout;
|
@@ -372,33 +454,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
372
454
|
if (wrapper->connect_timeout)
|
373
455
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
374
456
|
if (rv == Qfalse)
|
375
|
-
|
457
|
+
rb_raise_mysql2_error(wrapper);
|
376
458
|
}
|
377
459
|
|
378
460
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
379
|
-
wrapper->connected = 1;
|
380
461
|
return self;
|
381
462
|
}
|
382
463
|
|
383
464
|
/*
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
387
|
-
*
|
465
|
+
* Immediately disconnect from the server; normally the garbage collector
|
466
|
+
* will disconnect automatically when a connection is no longer needed.
|
467
|
+
* Explicitly closing this will free up server resources sooner than waiting
|
468
|
+
* for the garbage collector.
|
388
469
|
*
|
389
|
-
* @
|
390
|
-
* @return [void]
|
470
|
+
* @return [nil]
|
391
471
|
*/
|
392
472
|
static VALUE rb_mysql_client_close(VALUE self) {
|
393
473
|
GET_CLIENT(self);
|
394
474
|
|
395
|
-
if (wrapper->
|
475
|
+
if (wrapper->client) {
|
396
476
|
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
397
477
|
}
|
398
478
|
|
399
479
|
return Qnil;
|
400
480
|
}
|
401
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
|
+
|
402
492
|
/*
|
403
493
|
* mysql_send_query is unlikely to block since most queries are small
|
404
494
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
@@ -418,8 +508,8 @@ static VALUE do_send_query(void *args) {
|
|
418
508
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
419
509
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
420
510
|
/* an error occurred, we're not active anymore */
|
421
|
-
|
422
|
-
|
511
|
+
wrapper->active_thread = Qnil;
|
512
|
+
rb_raise_mysql2_error(wrapper);
|
423
513
|
}
|
424
514
|
return Qnil;
|
425
515
|
}
|
@@ -431,7 +521,7 @@ static VALUE do_send_query(void *args) {
|
|
431
521
|
*/
|
432
522
|
static void *nogvl_read_query_result(void *ptr) {
|
433
523
|
MYSQL * client = ptr;
|
434
|
-
|
524
|
+
bool res = mysql_read_query_result(client);
|
435
525
|
|
436
526
|
return (void *)(res == 0 ? Qtrue : Qfalse);
|
437
527
|
}
|
@@ -448,7 +538,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
448
538
|
|
449
539
|
/* once our result is stored off, this connection is
|
450
540
|
ready for another command to be issued */
|
451
|
-
|
541
|
+
wrapper->active_thread = Qnil;
|
452
542
|
|
453
543
|
return result;
|
454
544
|
}
|
@@ -480,8 +570,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
480
570
|
REQUIRE_CONNECTED(wrapper);
|
481
571
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
482
572
|
/* an error occurred, mark this connection inactive */
|
483
|
-
|
484
|
-
|
573
|
+
wrapper->active_thread = Qnil;
|
574
|
+
rb_raise_mysql2_error(wrapper);
|
485
575
|
}
|
486
576
|
|
487
577
|
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
@@ -493,7 +583,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
493
583
|
|
494
584
|
if (result == NULL) {
|
495
585
|
if (mysql_errno(wrapper->client) != 0) {
|
496
|
-
|
586
|
+
wrapper->active_thread = Qnil;
|
497
587
|
rb_raise_mysql2_error(wrapper);
|
498
588
|
}
|
499
589
|
/* no data and no error, so query was not a SELECT */
|
@@ -517,15 +607,17 @@ struct async_query_args {
|
|
517
607
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
518
608
|
GET_CLIENT(self);
|
519
609
|
|
520
|
-
|
521
|
-
wrapper->connected = 0;
|
610
|
+
wrapper->active_thread = Qnil;
|
522
611
|
|
523
612
|
/* Invalidate the MySQL socket to prevent further communication.
|
524
613
|
* The GC will come along later and call mysql_close to free it.
|
525
614
|
*/
|
526
|
-
if (
|
527
|
-
|
528
|
-
|
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;
|
529
621
|
}
|
530
622
|
|
531
623
|
rb_exc_raise(error);
|
@@ -574,26 +666,32 @@ static VALUE do_query(void *args) {
|
|
574
666
|
|
575
667
|
return Qnil;
|
576
668
|
}
|
577
|
-
#
|
578
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
579
|
-
VALUE self = args;
|
580
|
-
MYSQL_RES *result;
|
669
|
+
#endif
|
581
670
|
|
671
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
582
672
|
GET_CLIENT(self);
|
583
673
|
|
674
|
+
/* Check if execution terminated while result was still being read. */
|
584
675
|
if (!NIL_P(wrapper->active_thread)) {
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
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;
|
690
|
+
wrapper->active_thread = Qnil;
|
592
691
|
}
|
593
692
|
|
594
693
|
return Qnil;
|
595
694
|
}
|
596
|
-
#endif
|
597
695
|
|
598
696
|
void rb_mysql_client_set_active_thread(VALUE self) {
|
599
697
|
VALUE thread_current = rb_thread_current();
|
@@ -687,13 +785,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
687
785
|
|
688
786
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
689
787
|
|
690
|
-
return rb_mysql_client_async_result
|
788
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
691
789
|
}
|
692
790
|
#else
|
693
791
|
do_send_query(&args);
|
694
792
|
|
695
793
|
/* this will just block until the result is ready */
|
696
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
794
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
697
795
|
#endif
|
698
796
|
}
|
699
797
|
|
@@ -752,7 +850,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
752
850
|
const void *retval = NULL;
|
753
851
|
unsigned int intval = 0;
|
754
852
|
const char * charval = NULL;
|
755
|
-
|
853
|
+
bool boolval;
|
756
854
|
|
757
855
|
GET_CLIENT(self);
|
758
856
|
|
@@ -787,10 +885,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
787
885
|
retval = &boolval;
|
788
886
|
break;
|
789
887
|
|
888
|
+
#ifdef MYSQL_SECURE_AUTH
|
790
889
|
case MYSQL_SECURE_AUTH:
|
791
890
|
boolval = (value == Qfalse ? 0 : 1);
|
792
891
|
retval = &boolval;
|
793
892
|
break;
|
893
|
+
#endif
|
794
894
|
|
795
895
|
case MYSQL_READ_DEFAULT_FILE:
|
796
896
|
charval = (const char *)StringValueCStr(value);
|
@@ -807,6 +907,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
807
907
|
retval = charval;
|
808
908
|
break;
|
809
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
|
+
|
810
917
|
default:
|
811
918
|
return Qfalse;
|
812
919
|
}
|
@@ -996,7 +1103,7 @@ static void *nogvl_ping(void *ptr) {
|
|
996
1103
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
997
1104
|
GET_CLIENT(self);
|
998
1105
|
|
999
|
-
if (!wrapper
|
1106
|
+
if (!CONNECTED(wrapper)) {
|
1000
1107
|
return Qfalse;
|
1001
1108
|
} else {
|
1002
1109
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
@@ -1011,10 +1118,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1011
1118
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1012
1119
|
{
|
1013
1120
|
GET_CLIENT(self);
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1121
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1122
|
+
return Qfalse;
|
1123
|
+
else
|
1124
|
+
return Qtrue;
|
1018
1125
|
}
|
1019
1126
|
|
1020
1127
|
/* call-seq:
|
@@ -1081,6 +1188,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1081
1188
|
}
|
1082
1189
|
#endif
|
1083
1190
|
|
1191
|
+
/* call-seq:
|
1192
|
+
* client.automatic_close?
|
1193
|
+
*
|
1194
|
+
* @return [Boolean]
|
1195
|
+
*/
|
1196
|
+
static VALUE get_automatic_close(VALUE self) {
|
1197
|
+
GET_CLIENT(self);
|
1198
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
/* call-seq:
|
1202
|
+
* client.automatic_close = false
|
1203
|
+
*
|
1204
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1205
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1206
|
+
* call +close+ when the connection is no longer needed.
|
1207
|
+
*
|
1208
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1209
|
+
*/
|
1210
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1211
|
+
GET_CLIENT(self);
|
1212
|
+
if (RTEST(value)) {
|
1213
|
+
wrapper->automatic_close = 1;
|
1214
|
+
} else {
|
1215
|
+
#ifndef _WIN32
|
1216
|
+
wrapper->automatic_close = 0;
|
1217
|
+
#else
|
1218
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1219
|
+
#endif
|
1220
|
+
}
|
1221
|
+
return value;
|
1222
|
+
}
|
1223
|
+
|
1084
1224
|
/* call-seq:
|
1085
1225
|
* client.reconnect = true
|
1086
1226
|
*
|
@@ -1139,6 +1279,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1139
1279
|
#endif
|
1140
1280
|
GET_CLIENT(self);
|
1141
1281
|
|
1282
|
+
Check_Type(value, T_STRING);
|
1142
1283
|
charset_name = RSTRING_PTR(value);
|
1143
1284
|
|
1144
1285
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -1175,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1175
1316
|
}
|
1176
1317
|
|
1177
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
|
1178
1321
|
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
|
1322
|
+
#else
|
1323
|
+
return Qfalse;
|
1324
|
+
#endif
|
1179
1325
|
}
|
1180
1326
|
|
1181
1327
|
static VALUE set_read_default_file(VALUE self, VALUE value) {
|
@@ -1190,12 +1336,20 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1190
1336
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1191
1337
|
}
|
1192
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
|
+
|
1193
1347
|
static VALUE initialize_ext(VALUE self) {
|
1194
1348
|
GET_CLIENT(self);
|
1195
1349
|
|
1196
1350
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1197
1351
|
/* TODO: warning - not enough memory? */
|
1198
|
-
|
1352
|
+
rb_raise_mysql2_error(wrapper);
|
1199
1353
|
}
|
1200
1354
|
|
1201
1355
|
wrapper->initialized = 1;
|
@@ -1250,6 +1404,7 @@ void init_mysql2_client() {
|
|
1250
1404
|
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1251
1405
|
|
1252
1406
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1407
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1253
1408
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1254
1409
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1255
1410
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
@@ -1264,9 +1419,12 @@ void init_mysql2_client() {
|
|
1264
1419
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1265
1420
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1266
1421
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1422
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1423
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1267
1424
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1268
1425
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1269
1426
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1427
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1270
1428
|
#ifdef HAVE_RUBY_ENCODING_H
|
1271
1429
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1272
1430
|
#endif
|
@@ -1281,6 +1439,8 @@ void init_mysql2_client() {
|
|
1281
1439
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1282
1440
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1283
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);
|
1284
1444
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1285
1445
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1286
1446
|
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
@@ -1302,6 +1462,10 @@ void init_mysql2_client() {
|
|
1302
1462
|
#ifdef CLIENT_LONG_PASSWORD
|
1303
1463
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1304
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));
|
1305
1469
|
#endif
|
1306
1470
|
|
1307
1471
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1408,4 +1572,31 @@ void init_mysql2_client() {
|
|
1408
1572
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1409
1573
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1410
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
|
1411
1602
|
}
|
data/ext/mysql2/client.h
CHANGED
@@ -43,25 +43,15 @@ typedef struct {
|
|
43
43
|
int reconnect_enabled;
|
44
44
|
unsigned int connect_timeout;
|
45
45
|
int active;
|
46
|
-
int
|
46
|
+
int automatic_close;
|
47
47
|
int initialized;
|
48
48
|
int refcount;
|
49
|
-
int
|
49
|
+
int closed;
|
50
50
|
MYSQL *client;
|
51
51
|
} mysql_client_wrapper;
|
52
52
|
|
53
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
54
|
-
REQUIRE_INITIALIZED(wrapper) \
|
55
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
56
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
57
|
-
}
|
58
|
-
|
59
53
|
void rb_mysql_client_set_active_thread(VALUE self);
|
60
54
|
|
61
|
-
#define MARK_CONN_INACTIVE(conn) do {\
|
62
|
-
wrapper->active_thread = Qnil; \
|
63
|
-
} while(0)
|
64
|
-
|
65
55
|
#define GET_CLIENT(self) \
|
66
56
|
mysql_client_wrapper *wrapper; \
|
67
57
|
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -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
|
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 = [
|
data/ext/mysql2/mysql2_ext.h
CHANGED
@@ -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
|