mysql2 0.5.2 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +145 -44
- data/ext/mysql2/client.c +236 -58
- data/ext/mysql2/client.h +9 -2
- data/ext/mysql2/extconf.rb +62 -7
- data/ext/mysql2/mysql2_ext.c +6 -1
- data/ext/mysql2/mysql2_ext.h +13 -0
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -55
- data/ext/mysql2/mysql_enc_to_ruby.h +71 -5
- data/ext/mysql2/result.c +287 -22
- data/ext/mysql2/result.h +1 -0
- data/ext/mysql2/statement.c +63 -14
- data/lib/mysql2/client.rb +24 -3
- data/lib/mysql2/error.rb +4 -3
- data/lib/mysql2/statement.rb +1 -3
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +8 -3
- data/support/3A79BD29.asc +49 -0
- data/support/5072E1F5.asc +5 -5
- data/support/C74CD1D8.asc +104 -0
- data/support/mysql_enc_to_ruby.rb +7 -1
- data/support/ruby_enc_to_mysql.rb +3 -0
- metadata +15 -59
- data/examples/eventmachine.rb +0 -19
- data/examples/threaded.rb +0 -16
- data/spec/configuration.yml.example +0 -11
- data/spec/em/em_spec.rb +0 -135
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -1072
- data/spec/mysql2/error_spec.rb +0 -78
- data/spec/mysql2/result_spec.rb +0 -485
- data/spec/mysql2/statement_spec.rb +0 -712
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -112
- data/spec/ssl/ca-cert.pem +0 -17
- data/spec/ssl/ca-key.pem +0 -27
- data/spec/ssl/ca.cnf +0 -22
- data/spec/ssl/cert.cnf +0 -22
- data/spec/ssl/client-cert.pem +0 -17
- data/spec/ssl/client-key.pem +0 -27
- data/spec/ssl/client-req.pem +0 -15
- data/spec/ssl/gen_certs.sh +0 -48
- data/spec/ssl/pkcs8-client-key.pem +0 -28
- data/spec/ssl/pkcs8-server-key.pem +0 -28
- data/spec/ssl/server-cert.pem +0 -17
- data/spec/ssl/server-key.pem +0 -27
- data/spec/ssl/server-req.pem +0 -15
- data/spec/test_data +0 -1
data/ext/mysql2/client.c
CHANGED
@@ -18,7 +18,8 @@ VALUE cMysql2Client;
|
|
18
18
|
extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
|
19
19
|
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
20
|
static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
|
21
|
-
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
|
21
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
|
22
|
+
intern_current_query_options, intern_read_timeout;
|
22
23
|
|
23
24
|
#define REQUIRE_INITIALIZED(wrapper) \
|
24
25
|
if (!wrapper->initialized) { \
|
@@ -44,7 +45,7 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
|
|
44
45
|
}
|
45
46
|
|
46
47
|
/*
|
47
|
-
*
|
48
|
+
* compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
48
49
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
49
50
|
* linking against the server itself
|
50
51
|
*/
|
@@ -57,8 +58,23 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
|
|
57
58
|
#endif
|
58
59
|
|
59
60
|
/*
|
60
|
-
*
|
61
|
+
* mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
|
62
|
+
* while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
|
63
|
+
* This is a hack to take care of both clients.
|
61
64
|
*/
|
65
|
+
#if defined(CLIENT_SESSION_TRACK)
|
66
|
+
#elif defined(CLIENT_SESSION_TRACKING)
|
67
|
+
#define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
|
68
|
+
#define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
|
69
|
+
#endif
|
70
|
+
|
71
|
+
/*
|
72
|
+
* compatibility with mysql-connector-c 6.1.x, MySQL 5.7.3 - 5.7.10 & with MariaDB 10.x and later.
|
73
|
+
*/
|
74
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
|
75
|
+
#define SSL_MODE_VERIFY_IDENTITY 5
|
76
|
+
#define HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
77
|
+
#endif
|
62
78
|
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
63
79
|
#define SSL_MODE_DISABLED 1
|
64
80
|
#define SSL_MODE_REQUIRED 3
|
@@ -104,40 +120,80 @@ struct nogvl_select_db_args {
|
|
104
120
|
|
105
121
|
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
106
122
|
unsigned long version = mysql_get_client_version();
|
123
|
+
const char *version_str = mysql_get_client_info();
|
107
124
|
|
108
|
-
|
109
|
-
|
125
|
+
/* Warn about versions that are known to be incomplete; these are pretty
|
126
|
+
* ancient, we want people to upgrade if they need SSL/TLS to work
|
127
|
+
*
|
128
|
+
* MySQL 5.x before 5.6.30 -- ssl_mode introduced but not fully working until 5.6.36)
|
129
|
+
* MySQL 5.7 before 5.7.3 -- ssl_mode introduced but not fully working until 5.7.11)
|
130
|
+
*/
|
131
|
+
if ((version >= 50000 && version < 50630) || (version >= 50700 && version < 50703)) {
|
132
|
+
rb_warn("Your mysql client library version %s does not support setting ssl_mode; full support comes with 5.6.36+, 5.7.11+, 8.0+", version_str);
|
110
133
|
return Qnil;
|
111
134
|
}
|
112
|
-
|
135
|
+
|
136
|
+
/* For these versions, map from the options we're exposing to Ruby to the constant available:
|
137
|
+
* ssl_mode: :verify_identity to MYSQL_OPT_SSL_VERIFY_SERVER_CERT = 1
|
138
|
+
* ssl_mode: :required to MYSQL_OPT_SSL_ENFORCE = 1
|
139
|
+
* ssl_mode: :disabled to MYSQL_OPT_SSL_ENFORCE = 0
|
140
|
+
*/
|
141
|
+
#if defined(HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT) || defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE)
|
113
142
|
GET_CLIENT(self);
|
114
|
-
int val = NUM2INT(
|
115
|
-
|
143
|
+
int val = NUM2INT(setting);
|
144
|
+
|
145
|
+
/* Expected code path for MariaDB 10.x and MariaDB Connector/C 3.x
|
146
|
+
* Workaround code path for MySQL 5.7.3 - 5.7.10 and MySQL Connector/C 6.1.3 - 6.1.x
|
147
|
+
*/
|
148
|
+
if (version >= 100000 // MariaDB (all versions numbered 10.x)
|
149
|
+
|| (version >= 30000 && version < 40000) // MariaDB Connector/C (all versions numbered 3.x)
|
150
|
+
|| (version >= 50703 && version < 50711) // Workaround for MySQL 5.7.3 - 5.7.10
|
151
|
+
|| (version >= 60103 && version < 60200)) { // Workaround for MySQL Connector/C 6.1.3 - 6.1.x
|
152
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
|
153
|
+
if (val == SSL_MODE_VERIFY_IDENTITY) {
|
154
|
+
my_bool b = 1;
|
155
|
+
int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &b);
|
156
|
+
return INT2NUM(result);
|
157
|
+
}
|
158
|
+
#endif
|
159
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
116
160
|
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
117
|
-
my_bool b = (
|
118
|
-
int result = mysql_options(
|
161
|
+
my_bool b = (val == SSL_MODE_REQUIRED);
|
162
|
+
int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b);
|
119
163
|
return INT2NUM(result);
|
120
|
-
} else {
|
121
|
-
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
122
|
-
return Qnil;
|
123
164
|
}
|
165
|
+
#endif
|
166
|
+
rb_warn("Your mysql client library version %s does not support ssl_mode %d", version_str, val);
|
167
|
+
return Qnil;
|
168
|
+
} else {
|
169
|
+
rb_warn("Your mysql client library version %s does not support ssl_mode as expected", version_str);
|
170
|
+
return Qnil;
|
124
171
|
}
|
125
172
|
#endif
|
173
|
+
|
174
|
+
/* For other versions -- known to be MySQL 5.6.36+, 5.7.11+, 8.0+
|
175
|
+
* pass the value of the argument to MYSQL_OPT_SSL_MODE -- note the code
|
176
|
+
* mapping from atoms / constants is in the MySQL::Client Ruby class
|
177
|
+
*/
|
126
178
|
#ifdef FULL_SSL_MODE_SUPPORT
|
127
179
|
GET_CLIENT(self);
|
128
|
-
int val = NUM2INT(
|
180
|
+
int val = NUM2INT(setting);
|
129
181
|
|
130
182
|
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
131
183
|
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
132
184
|
}
|
133
|
-
int result = mysql_options(
|
185
|
+
int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_MODE, &val);
|
134
186
|
|
135
187
|
return INT2NUM(result);
|
136
188
|
#endif
|
189
|
+
|
190
|
+
// Warn if we get this far
|
137
191
|
#ifdef NO_SSL_MODE_SUPPORT
|
192
|
+
rb_warn("Your mysql client library does not support setting ssl_mode");
|
138
193
|
return Qnil;
|
139
194
|
#endif
|
140
195
|
}
|
196
|
+
|
141
197
|
/*
|
142
198
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
143
199
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -163,14 +219,50 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
|
163
219
|
static void rb_mysql_client_mark(void * wrapper) {
|
164
220
|
mysql_client_wrapper * w = wrapper;
|
165
221
|
if (w) {
|
166
|
-
|
167
|
-
|
222
|
+
rb_gc_mark_movable(w->encoding);
|
223
|
+
rb_gc_mark_movable(w->active_fiber);
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
/* this is called during GC */
|
228
|
+
static void rb_mysql_client_free(void *ptr) {
|
229
|
+
mysql_client_wrapper *wrapper = ptr;
|
230
|
+
decr_mysql2_client(wrapper);
|
231
|
+
}
|
232
|
+
|
233
|
+
static size_t rb_mysql_client_memsize(const void * wrapper) {
|
234
|
+
const mysql_client_wrapper * w = wrapper;
|
235
|
+
return sizeof(*w);
|
236
|
+
}
|
237
|
+
|
238
|
+
static void rb_mysql_client_compact(void * wrapper) {
|
239
|
+
mysql_client_wrapper * w = wrapper;
|
240
|
+
if (w) {
|
241
|
+
rb_mysql2_gc_location(w->encoding);
|
242
|
+
rb_mysql2_gc_location(w->active_fiber);
|
168
243
|
}
|
169
244
|
}
|
170
245
|
|
246
|
+
const rb_data_type_t rb_mysql_client_type = {
|
247
|
+
"rb_mysql_client",
|
248
|
+
{
|
249
|
+
rb_mysql_client_mark,
|
250
|
+
rb_mysql_client_free,
|
251
|
+
rb_mysql_client_memsize,
|
252
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
253
|
+
rb_mysql_client_compact,
|
254
|
+
#endif
|
255
|
+
},
|
256
|
+
0,
|
257
|
+
0,
|
258
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
259
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
260
|
+
#endif
|
261
|
+
};
|
262
|
+
|
171
263
|
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
172
264
|
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
|
173
|
-
VALUE rb_sql_state =
|
265
|
+
VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
|
174
266
|
VALUE e;
|
175
267
|
|
176
268
|
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
@@ -264,22 +356,16 @@ static VALUE invalidate_fd(int clientfd)
|
|
264
356
|
static void *nogvl_close(void *ptr) {
|
265
357
|
mysql_client_wrapper *wrapper = ptr;
|
266
358
|
|
267
|
-
if (!wrapper->closed) {
|
359
|
+
if (wrapper->initialized && !wrapper->closed) {
|
268
360
|
mysql_close(wrapper->client);
|
269
361
|
wrapper->closed = 1;
|
270
362
|
wrapper->reconnect_enabled = 0;
|
271
|
-
wrapper->
|
363
|
+
wrapper->active_fiber = Qnil;
|
272
364
|
}
|
273
365
|
|
274
366
|
return NULL;
|
275
367
|
}
|
276
368
|
|
277
|
-
/* this is called during GC */
|
278
|
-
static void rb_mysql_client_free(void *ptr) {
|
279
|
-
mysql_client_wrapper *wrapper = ptr;
|
280
|
-
decr_mysql2_client(wrapper);
|
281
|
-
}
|
282
|
-
|
283
369
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
284
370
|
{
|
285
371
|
wrapper->refcount--;
|
@@ -311,16 +397,20 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
311
397
|
static VALUE allocate(VALUE klass) {
|
312
398
|
VALUE obj;
|
313
399
|
mysql_client_wrapper * wrapper;
|
400
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
401
|
+
obj = TypedData_Make_Struct(klass, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
|
402
|
+
#else
|
314
403
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
404
|
+
#endif
|
315
405
|
wrapper->encoding = Qnil;
|
316
|
-
wrapper->
|
406
|
+
wrapper->active_fiber = Qnil;
|
317
407
|
wrapper->automatic_close = 1;
|
318
408
|
wrapper->server_version = 0;
|
319
409
|
wrapper->reconnect_enabled = 0;
|
320
410
|
wrapper->connect_timeout = 0;
|
321
|
-
wrapper->initialized = 0; /*
|
411
|
+
wrapper->initialized = 0; /* will be set true after calling mysql_init */
|
412
|
+
wrapper->closed = 1; /* will be set false after calling mysql_real_connect */
|
322
413
|
wrapper->refcount = 1;
|
323
|
-
wrapper->closed = 0;
|
324
414
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
325
415
|
|
326
416
|
return obj;
|
@@ -462,6 +552,7 @@ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VA
|
|
462
552
|
rb_raise_mysql2_error(wrapper);
|
463
553
|
}
|
464
554
|
|
555
|
+
wrapper->closed = 0;
|
465
556
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
466
557
|
return self;
|
467
558
|
}
|
@@ -508,12 +599,12 @@ static void *nogvl_send_query(void *ptr) {
|
|
508
599
|
return (void*)(rv == 0 ? Qtrue : Qfalse);
|
509
600
|
}
|
510
601
|
|
511
|
-
static VALUE do_send_query(
|
512
|
-
struct nogvl_send_query_args *query_args = args;
|
602
|
+
static VALUE do_send_query(VALUE args) {
|
603
|
+
struct nogvl_send_query_args *query_args = (void *)args;
|
513
604
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
514
|
-
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query,
|
605
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
|
515
606
|
/* an error occurred, we're not active anymore */
|
516
|
-
wrapper->
|
607
|
+
wrapper->active_fiber = Qnil;
|
517
608
|
rb_raise_mysql2_error(wrapper);
|
518
609
|
}
|
519
610
|
return Qnil;
|
@@ -543,7 +634,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
543
634
|
|
544
635
|
/* once our result is stored off, this connection is
|
545
636
|
ready for another command to be issued */
|
546
|
-
wrapper->
|
637
|
+
wrapper->active_fiber = Qnil;
|
547
638
|
|
548
639
|
return result;
|
549
640
|
}
|
@@ -569,17 +660,17 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
569
660
|
GET_CLIENT(self);
|
570
661
|
|
571
662
|
/* if we're not waiting on a result, do nothing */
|
572
|
-
if (NIL_P(wrapper->
|
663
|
+
if (NIL_P(wrapper->active_fiber))
|
573
664
|
return Qnil;
|
574
665
|
|
575
666
|
REQUIRE_CONNECTED(wrapper);
|
576
667
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
577
668
|
/* an error occurred, mark this connection inactive */
|
578
|
-
wrapper->
|
669
|
+
wrapper->active_fiber = Qnil;
|
579
670
|
rb_raise_mysql2_error(wrapper);
|
580
671
|
}
|
581
672
|
|
582
|
-
is_streaming = rb_hash_aref(
|
673
|
+
is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
|
583
674
|
if (is_streaming == Qtrue) {
|
584
675
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
585
676
|
} else {
|
@@ -588,7 +679,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
588
679
|
|
589
680
|
if (result == NULL) {
|
590
681
|
if (mysql_errno(wrapper->client) != 0) {
|
591
|
-
wrapper->
|
682
|
+
wrapper->active_fiber = Qnil;
|
592
683
|
rb_raise_mysql2_error(wrapper);
|
593
684
|
}
|
594
685
|
/* no data and no error, so query was not a SELECT */
|
@@ -596,7 +687,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
596
687
|
}
|
597
688
|
|
598
689
|
// Duplicate the options hash and put the copy in the Result object
|
599
|
-
current = rb_hash_dup(
|
690
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
600
691
|
(void)RB_GC_GUARD(current);
|
601
692
|
Check_Type(current, T_HASH);
|
602
693
|
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
@@ -615,7 +706,7 @@ struct async_query_args {
|
|
615
706
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
616
707
|
GET_CLIENT(self);
|
617
708
|
|
618
|
-
wrapper->
|
709
|
+
wrapper->active_fiber = Qnil;
|
619
710
|
|
620
711
|
/* Invalidate the MySQL socket to prevent further communication.
|
621
712
|
* The GC will come along later and call mysql_close to free it.
|
@@ -631,15 +722,15 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
631
722
|
rb_exc_raise(error);
|
632
723
|
}
|
633
724
|
|
634
|
-
static VALUE do_query(
|
635
|
-
struct async_query_args *async_args = args;
|
725
|
+
static VALUE do_query(VALUE args) {
|
726
|
+
struct async_query_args *async_args = (void *)args;
|
636
727
|
struct timeval tv;
|
637
728
|
struct timeval *tvp;
|
638
729
|
long int sec;
|
639
730
|
int retval;
|
640
731
|
VALUE read_timeout;
|
641
732
|
|
642
|
-
read_timeout =
|
733
|
+
read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
|
643
734
|
|
644
735
|
tvp = NULL;
|
645
736
|
if (!NIL_P(read_timeout)) {
|
@@ -680,7 +771,7 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
|
|
680
771
|
GET_CLIENT(self);
|
681
772
|
|
682
773
|
/* Check if execution terminated while result was still being read. */
|
683
|
-
if (!NIL_P(wrapper->
|
774
|
+
if (!NIL_P(wrapper->active_fiber)) {
|
684
775
|
if (CONNECTED(wrapper)) {
|
685
776
|
/* Invalidate the MySQL socket to prevent further communication. */
|
686
777
|
#ifndef _WIN32
|
@@ -695,24 +786,24 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
|
|
695
786
|
}
|
696
787
|
/* Skip mysql client check performed before command execution. */
|
697
788
|
wrapper->client->status = MYSQL_STATUS_READY;
|
698
|
-
wrapper->
|
789
|
+
wrapper->active_fiber = Qnil;
|
699
790
|
}
|
700
791
|
|
701
792
|
return Qnil;
|
702
793
|
}
|
703
794
|
|
704
|
-
void
|
705
|
-
VALUE
|
795
|
+
static void rb_mysql_client_set_active_fiber(VALUE self) {
|
796
|
+
VALUE fiber_current = rb_fiber_current();
|
706
797
|
GET_CLIENT(self);
|
707
798
|
|
708
799
|
// see if this connection is still waiting on a result from a previous query
|
709
|
-
if (NIL_P(wrapper->
|
800
|
+
if (NIL_P(wrapper->active_fiber)) {
|
710
801
|
// mark this connection active
|
711
|
-
wrapper->
|
712
|
-
} else if (wrapper->
|
802
|
+
wrapper->active_fiber = fiber_current;
|
803
|
+
} else if (wrapper->active_fiber == fiber_current) {
|
713
804
|
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
714
805
|
} else {
|
715
|
-
VALUE inspect = rb_inspect(wrapper->
|
806
|
+
VALUE inspect = rb_inspect(wrapper->active_fiber);
|
716
807
|
const char *thr = StringValueCStr(inspect);
|
717
808
|
|
718
809
|
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
@@ -767,7 +858,7 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
|
767
858
|
|
768
859
|
(void)RB_GC_GUARD(current);
|
769
860
|
Check_Type(current, T_HASH);
|
770
|
-
|
861
|
+
rb_ivar_set(self, intern_current_query_options, current);
|
771
862
|
|
772
863
|
Check_Type(sql, T_STRING);
|
773
864
|
/* ensure the string is in the encoding the connection is expecting */
|
@@ -776,10 +867,11 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
|
776
867
|
args.sql_len = RSTRING_LEN(args.sql);
|
777
868
|
args.wrapper = wrapper;
|
778
869
|
|
779
|
-
|
870
|
+
rb_mysql_client_set_active_fiber(self);
|
780
871
|
|
781
872
|
#ifndef _WIN32
|
782
873
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
874
|
+
(void)RB_GC_GUARD(sql);
|
783
875
|
|
784
876
|
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
785
877
|
return Qnil;
|
@@ -792,7 +884,8 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
|
792
884
|
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
793
885
|
}
|
794
886
|
#else
|
795
|
-
do_send_query(&args);
|
887
|
+
do_send_query((VALUE)&args);
|
888
|
+
(void)RB_GC_GUARD(sql);
|
796
889
|
|
797
890
|
/* this will just block until the result is ready */
|
798
891
|
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
@@ -903,6 +996,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
903
996
|
retval = charval;
|
904
997
|
break;
|
905
998
|
|
999
|
+
#ifdef HAVE_MYSQL_DEFAULT_AUTH
|
1000
|
+
case MYSQL_DEFAULT_AUTH:
|
1001
|
+
charval = (const char *)StringValueCStr(value);
|
1002
|
+
retval = charval;
|
1003
|
+
break;
|
1004
|
+
#endif
|
1005
|
+
|
906
1006
|
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
907
1007
|
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
908
1008
|
boolval = (value == Qfalse ? 0 : 1);
|
@@ -1011,6 +1111,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
|
|
1011
1111
|
return ULL2NUM(mysql_insert_id(wrapper->client));
|
1012
1112
|
}
|
1013
1113
|
|
1114
|
+
/* call-seq:
|
1115
|
+
* client.session_track
|
1116
|
+
*
|
1117
|
+
* Returns information about changes to the session state on the server.
|
1118
|
+
*/
|
1119
|
+
static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
|
1120
|
+
#ifdef CLIENT_SESSION_TRACK
|
1121
|
+
const char *data;
|
1122
|
+
size_t length;
|
1123
|
+
my_ulonglong retVal;
|
1124
|
+
GET_CLIENT(self);
|
1125
|
+
|
1126
|
+
REQUIRE_CONNECTED(wrapper);
|
1127
|
+
retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
|
1128
|
+
if (retVal != 0) {
|
1129
|
+
return Qnil;
|
1130
|
+
}
|
1131
|
+
VALUE rbAry = rb_ary_new();
|
1132
|
+
VALUE rbFirst = rb_str_new(data, length);
|
1133
|
+
rb_ary_push(rbAry, rbFirst);
|
1134
|
+
while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
|
1135
|
+
VALUE rbNext = rb_str_new(data, length);
|
1136
|
+
rb_ary_push(rbAry, rbNext);
|
1137
|
+
}
|
1138
|
+
return rbAry;
|
1139
|
+
#else
|
1140
|
+
return Qnil;
|
1141
|
+
#endif
|
1142
|
+
}
|
1143
|
+
|
1014
1144
|
/* call-seq:
|
1015
1145
|
* client.affected_rows
|
1016
1146
|
*
|
@@ -1174,7 +1304,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1174
1304
|
}
|
1175
1305
|
|
1176
1306
|
// Duplicate the options hash and put the copy in the Result object
|
1177
|
-
current = rb_hash_dup(
|
1307
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
1178
1308
|
(void)RB_GC_GUARD(current);
|
1179
1309
|
Check_Type(current, T_HASH);
|
1180
1310
|
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
@@ -1260,7 +1390,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
|
|
1260
1390
|
/* Set the instance variable here even though _mysql_client_options
|
1261
1391
|
might not succeed, because the timeout is used in other ways
|
1262
1392
|
elsewhere */
|
1263
|
-
|
1393
|
+
rb_ivar_set(self, intern_read_timeout, value);
|
1264
1394
|
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
|
1265
1395
|
}
|
1266
1396
|
|
@@ -1305,12 +1435,31 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1305
1435
|
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
1306
1436
|
GET_CLIENT(self);
|
1307
1437
|
|
1438
|
+
#ifdef HAVE_MYSQL_SSL_SET
|
1308
1439
|
mysql_ssl_set(wrapper->client,
|
1309
1440
|
NIL_P(key) ? NULL : StringValueCStr(key),
|
1310
1441
|
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1311
1442
|
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1312
1443
|
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1313
1444
|
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1445
|
+
#else
|
1446
|
+
/* mysql 8.3 does not provide mysql_ssl_set */
|
1447
|
+
if (!NIL_P(key)) {
|
1448
|
+
mysql_options(wrapper->client, MYSQL_OPT_SSL_KEY, StringValueCStr(key));
|
1449
|
+
}
|
1450
|
+
if (!NIL_P(cert)) {
|
1451
|
+
mysql_options(wrapper->client, MYSQL_OPT_SSL_CERT, StringValueCStr(cert));
|
1452
|
+
}
|
1453
|
+
if (!NIL_P(ca)) {
|
1454
|
+
mysql_options(wrapper->client, MYSQL_OPT_SSL_CA, StringValueCStr(ca));
|
1455
|
+
}
|
1456
|
+
if (!NIL_P(capath)) {
|
1457
|
+
mysql_options(wrapper->client, MYSQL_OPT_SSL_CAPATH, StringValueCStr(capath));
|
1458
|
+
}
|
1459
|
+
if (!NIL_P(cipher)) {
|
1460
|
+
mysql_options(wrapper->client, MYSQL_OPT_SSL_CIPHER, StringValueCStr(cipher));
|
1461
|
+
}
|
1462
|
+
#endif
|
1314
1463
|
|
1315
1464
|
return self;
|
1316
1465
|
}
|
@@ -1336,6 +1485,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1336
1485
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1337
1486
|
}
|
1338
1487
|
|
1488
|
+
static VALUE set_default_auth(VALUE self, VALUE value) {
|
1489
|
+
#ifdef HAVE_MYSQL_DEFAULT_AUTH
|
1490
|
+
return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
|
1491
|
+
#else
|
1492
|
+
rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
|
1493
|
+
#endif
|
1494
|
+
}
|
1495
|
+
|
1339
1496
|
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1340
1497
|
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1341
1498
|
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
@@ -1397,6 +1554,7 @@ void init_mysql2_client() {
|
|
1397
1554
|
mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
|
1398
1555
|
#endif
|
1399
1556
|
cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
1557
|
+
rb_global_variable(&cMysql2Client);
|
1400
1558
|
|
1401
1559
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1402
1560
|
|
@@ -1427,6 +1585,7 @@ void init_mysql2_client() {
|
|
1427
1585
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1428
1586
|
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1429
1587
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1588
|
+
rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
|
1430
1589
|
|
1431
1590
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1432
1591
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1437,6 +1596,7 @@ void init_mysql2_client() {
|
|
1437
1596
|
rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
|
1438
1597
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1439
1598
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1599
|
+
rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
|
1440
1600
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1441
1601
|
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1442
1602
|
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
@@ -1461,6 +1621,8 @@ void init_mysql2_client() {
|
|
1461
1621
|
intern_merge = rb_intern("merge");
|
1462
1622
|
intern_merge_bang = rb_intern("merge!");
|
1463
1623
|
intern_new_with_args = rb_intern("new_with_args");
|
1624
|
+
intern_current_query_options = rb_intern("@current_query_options");
|
1625
|
+
intern_read_timeout = rb_intern("@read_timeout");
|
1464
1626
|
|
1465
1627
|
#ifdef CLIENT_LONG_PASSWORD
|
1466
1628
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
@@ -1596,16 +1758,32 @@ void init_mysql2_client() {
|
|
1596
1758
|
INT2NUM(0));
|
1597
1759
|
#endif
|
1598
1760
|
|
1599
|
-
#
|
1761
|
+
#ifdef CLIENT_SESSION_TRACK
|
1762
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
|
1763
|
+
/* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
|
1764
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
|
1765
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
|
1766
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
|
1767
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
|
1768
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
|
1769
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
|
1770
|
+
#endif
|
1771
|
+
|
1772
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.6.36 and MySQL 5.7.11 and above
|
1600
1773
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1601
1774
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1602
1775
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1603
1776
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1604
1777
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1605
|
-
#
|
1778
|
+
#else
|
1779
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
|
1780
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1781
|
+
#endif
|
1782
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
|
1606
1783
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1607
1784
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1608
1785
|
#endif
|
1786
|
+
#endif
|
1609
1787
|
|
1610
1788
|
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1611
1789
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
data/ext/mysql2/client.h
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
|
4
4
|
typedef struct {
|
5
5
|
VALUE encoding;
|
6
|
-
VALUE
|
6
|
+
VALUE active_fiber; /* rb_fiber_current() or Qnil */
|
7
7
|
long server_version;
|
8
8
|
int reconnect_enabled;
|
9
9
|
unsigned int connect_timeout;
|
@@ -15,12 +15,19 @@ typedef struct {
|
|
15
15
|
MYSQL *client;
|
16
16
|
} mysql_client_wrapper;
|
17
17
|
|
18
|
-
void rb_mysql_client_set_active_thread(VALUE self);
|
19
18
|
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result);
|
20
19
|
|
20
|
+
extern const rb_data_type_t rb_mysql_client_type;
|
21
|
+
|
22
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
23
|
+
#define GET_CLIENT(self) \
|
24
|
+
mysql_client_wrapper *wrapper; \
|
25
|
+
TypedData_Get_Struct(self, mysql_client_wrapper, &rb_mysql_client_type, wrapper);
|
26
|
+
#else
|
21
27
|
#define GET_CLIENT(self) \
|
22
28
|
mysql_client_wrapper *wrapper; \
|
23
29
|
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
|
30
|
+
#endif
|
24
31
|
|
25
32
|
void init_mysql2_client(void);
|
26
33
|
void decr_mysql2_client(mysql_client_wrapper *wrapper);
|