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