mysql2 0.5.2 → 0.5.6
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 +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);
|