mysql2 0.5.3 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +107 -22
- data/ext/mysql2/client.c +214 -53
- data/ext/mysql2/client.h +9 -2
- data/ext/mysql2/extconf.rb +61 -6
- 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 +4 -3
- data/ext/mysql2/mysql_enc_to_ruby.h +15 -0
- data/ext/mysql2/result.c +270 -11
- data/ext/mysql2/result.h +1 -0
- data/ext/mysql2/statement.c +59 -12
- data/lib/mysql2/client.rb +23 -2
- data/lib/mysql2/error.rb +1 -0
- data/lib/mysql2/statement.rb +1 -3
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +2 -0
- data/support/3A79BD29.asc +49 -0
- data/support/C74CD1D8.asc +104 -0
- data/support/mysql_enc_to_ruby.rb +2 -1
- data/support/ruby_enc_to_mysql.rb +1 -0
- metadata +14 -7
data/ext/mysql2/client.c
CHANGED
@@ -45,7 +45,7 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
|
|
45
45
|
}
|
46
46
|
|
47
47
|
/*
|
48
|
-
*
|
48
|
+
* compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
49
49
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
50
50
|
* linking against the server itself
|
51
51
|
*/
|
@@ -58,8 +58,23 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
|
|
58
58
|
#endif
|
59
59
|
|
60
60
|
/*
|
61
|
-
*
|
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.
|
62
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
|
63
78
|
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
64
79
|
#define SSL_MODE_DISABLED 1
|
65
80
|
#define SSL_MODE_REQUIRED 3
|
@@ -105,44 +120,80 @@ struct nogvl_select_db_args {
|
|
105
120
|
|
106
121
|
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
107
122
|
unsigned long version = mysql_get_client_version();
|
123
|
+
const char *version_str = mysql_get_client_info();
|
108
124
|
|
109
|
-
|
110
|
-
|
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);
|
111
133
|
return Qnil;
|
112
134
|
}
|
113
|
-
|
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)
|
114
142
|
GET_CLIENT(self);
|
115
|
-
int val = NUM2INT(
|
116
|
-
|
117
|
-
|
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
|
118
160
|
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
119
|
-
my_bool b = (
|
120
|
-
int result = mysql_options(
|
161
|
+
my_bool b = (val == SSL_MODE_REQUIRED);
|
162
|
+
int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b);
|
121
163
|
return INT2NUM(result);
|
122
|
-
} else {
|
123
|
-
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
124
|
-
return Qnil;
|
125
164
|
}
|
165
|
+
#endif
|
166
|
+
rb_warn("Your mysql client library version %s does not support ssl_mode %d", version_str, val);
|
167
|
+
return Qnil;
|
126
168
|
} else {
|
127
|
-
rb_warn(
|
169
|
+
rb_warn("Your mysql client library version %s does not support ssl_mode as expected", version_str);
|
128
170
|
return Qnil;
|
129
171
|
}
|
130
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
|
+
*/
|
131
178
|
#ifdef FULL_SSL_MODE_SUPPORT
|
132
179
|
GET_CLIENT(self);
|
133
|
-
int val = NUM2INT(
|
180
|
+
int val = NUM2INT(setting);
|
134
181
|
|
135
182
|
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
136
183
|
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
137
184
|
}
|
138
|
-
int result = mysql_options(
|
185
|
+
int result = mysql_options(wrapper->client, MYSQL_OPT_SSL_MODE, &val);
|
139
186
|
|
140
187
|
return INT2NUM(result);
|
141
188
|
#endif
|
189
|
+
|
190
|
+
// Warn if we get this far
|
142
191
|
#ifdef NO_SSL_MODE_SUPPORT
|
192
|
+
rb_warn("Your mysql client library does not support setting ssl_mode");
|
143
193
|
return Qnil;
|
144
194
|
#endif
|
145
195
|
}
|
196
|
+
|
146
197
|
/*
|
147
198
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
148
199
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -168,14 +219,50 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
|
168
219
|
static void rb_mysql_client_mark(void * wrapper) {
|
169
220
|
mysql_client_wrapper * w = wrapper;
|
170
221
|
if (w) {
|
171
|
-
|
172
|
-
|
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);
|
173
243
|
}
|
174
244
|
}
|
175
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
|
+
|
176
263
|
static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
177
264
|
VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
|
178
|
-
VALUE rb_sql_state =
|
265
|
+
VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
|
179
266
|
VALUE e;
|
180
267
|
|
181
268
|
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
@@ -269,22 +356,16 @@ static VALUE invalidate_fd(int clientfd)
|
|
269
356
|
static void *nogvl_close(void *ptr) {
|
270
357
|
mysql_client_wrapper *wrapper = ptr;
|
271
358
|
|
272
|
-
if (!wrapper->closed) {
|
359
|
+
if (wrapper->initialized && !wrapper->closed) {
|
273
360
|
mysql_close(wrapper->client);
|
274
361
|
wrapper->closed = 1;
|
275
362
|
wrapper->reconnect_enabled = 0;
|
276
|
-
wrapper->
|
363
|
+
wrapper->active_fiber = Qnil;
|
277
364
|
}
|
278
365
|
|
279
366
|
return NULL;
|
280
367
|
}
|
281
368
|
|
282
|
-
/* this is called during GC */
|
283
|
-
static void rb_mysql_client_free(void *ptr) {
|
284
|
-
mysql_client_wrapper *wrapper = ptr;
|
285
|
-
decr_mysql2_client(wrapper);
|
286
|
-
}
|
287
|
-
|
288
369
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
289
370
|
{
|
290
371
|
wrapper->refcount--;
|
@@ -316,16 +397,20 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
316
397
|
static VALUE allocate(VALUE klass) {
|
317
398
|
VALUE obj;
|
318
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
|
319
403
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
404
|
+
#endif
|
320
405
|
wrapper->encoding = Qnil;
|
321
|
-
wrapper->
|
406
|
+
wrapper->active_fiber = Qnil;
|
322
407
|
wrapper->automatic_close = 1;
|
323
408
|
wrapper->server_version = 0;
|
324
409
|
wrapper->reconnect_enabled = 0;
|
325
410
|
wrapper->connect_timeout = 0;
|
326
|
-
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 */
|
327
413
|
wrapper->refcount = 1;
|
328
|
-
wrapper->closed = 0;
|
329
414
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
330
415
|
|
331
416
|
return obj;
|
@@ -467,6 +552,7 @@ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VA
|
|
467
552
|
rb_raise_mysql2_error(wrapper);
|
468
553
|
}
|
469
554
|
|
555
|
+
wrapper->closed = 0;
|
470
556
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
471
557
|
return self;
|
472
558
|
}
|
@@ -513,12 +599,12 @@ static void *nogvl_send_query(void *ptr) {
|
|
513
599
|
return (void*)(rv == 0 ? Qtrue : Qfalse);
|
514
600
|
}
|
515
601
|
|
516
|
-
static VALUE do_send_query(
|
517
|
-
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;
|
518
604
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
519
|
-
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) {
|
520
606
|
/* an error occurred, we're not active anymore */
|
521
|
-
wrapper->
|
607
|
+
wrapper->active_fiber = Qnil;
|
522
608
|
rb_raise_mysql2_error(wrapper);
|
523
609
|
}
|
524
610
|
return Qnil;
|
@@ -548,7 +634,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
548
634
|
|
549
635
|
/* once our result is stored off, this connection is
|
550
636
|
ready for another command to be issued */
|
551
|
-
wrapper->
|
637
|
+
wrapper->active_fiber = Qnil;
|
552
638
|
|
553
639
|
return result;
|
554
640
|
}
|
@@ -574,13 +660,13 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
574
660
|
GET_CLIENT(self);
|
575
661
|
|
576
662
|
/* if we're not waiting on a result, do nothing */
|
577
|
-
if (NIL_P(wrapper->
|
663
|
+
if (NIL_P(wrapper->active_fiber))
|
578
664
|
return Qnil;
|
579
665
|
|
580
666
|
REQUIRE_CONNECTED(wrapper);
|
581
667
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
582
668
|
/* an error occurred, mark this connection inactive */
|
583
|
-
wrapper->
|
669
|
+
wrapper->active_fiber = Qnil;
|
584
670
|
rb_raise_mysql2_error(wrapper);
|
585
671
|
}
|
586
672
|
|
@@ -593,7 +679,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
593
679
|
|
594
680
|
if (result == NULL) {
|
595
681
|
if (mysql_errno(wrapper->client) != 0) {
|
596
|
-
wrapper->
|
682
|
+
wrapper->active_fiber = Qnil;
|
597
683
|
rb_raise_mysql2_error(wrapper);
|
598
684
|
}
|
599
685
|
/* no data and no error, so query was not a SELECT */
|
@@ -620,7 +706,7 @@ struct async_query_args {
|
|
620
706
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
621
707
|
GET_CLIENT(self);
|
622
708
|
|
623
|
-
wrapper->
|
709
|
+
wrapper->active_fiber = Qnil;
|
624
710
|
|
625
711
|
/* Invalidate the MySQL socket to prevent further communication.
|
626
712
|
* The GC will come along later and call mysql_close to free it.
|
@@ -636,8 +722,8 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
636
722
|
rb_exc_raise(error);
|
637
723
|
}
|
638
724
|
|
639
|
-
static VALUE do_query(
|
640
|
-
struct async_query_args *async_args = args;
|
725
|
+
static VALUE do_query(VALUE args) {
|
726
|
+
struct async_query_args *async_args = (void *)args;
|
641
727
|
struct timeval tv;
|
642
728
|
struct timeval *tvp;
|
643
729
|
long int sec;
|
@@ -685,7 +771,7 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
|
|
685
771
|
GET_CLIENT(self);
|
686
772
|
|
687
773
|
/* Check if execution terminated while result was still being read. */
|
688
|
-
if (!NIL_P(wrapper->
|
774
|
+
if (!NIL_P(wrapper->active_fiber)) {
|
689
775
|
if (CONNECTED(wrapper)) {
|
690
776
|
/* Invalidate the MySQL socket to prevent further communication. */
|
691
777
|
#ifndef _WIN32
|
@@ -700,24 +786,24 @@ static VALUE disconnect_and_mark_inactive(VALUE self) {
|
|
700
786
|
}
|
701
787
|
/* Skip mysql client check performed before command execution. */
|
702
788
|
wrapper->client->status = MYSQL_STATUS_READY;
|
703
|
-
wrapper->
|
789
|
+
wrapper->active_fiber = Qnil;
|
704
790
|
}
|
705
791
|
|
706
792
|
return Qnil;
|
707
793
|
}
|
708
794
|
|
709
|
-
void
|
710
|
-
VALUE
|
795
|
+
static void rb_mysql_client_set_active_fiber(VALUE self) {
|
796
|
+
VALUE fiber_current = rb_fiber_current();
|
711
797
|
GET_CLIENT(self);
|
712
798
|
|
713
799
|
// see if this connection is still waiting on a result from a previous query
|
714
|
-
if (NIL_P(wrapper->
|
800
|
+
if (NIL_P(wrapper->active_fiber)) {
|
715
801
|
// mark this connection active
|
716
|
-
wrapper->
|
717
|
-
} else if (wrapper->
|
802
|
+
wrapper->active_fiber = fiber_current;
|
803
|
+
} else if (wrapper->active_fiber == fiber_current) {
|
718
804
|
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
719
805
|
} else {
|
720
|
-
VALUE inspect = rb_inspect(wrapper->
|
806
|
+
VALUE inspect = rb_inspect(wrapper->active_fiber);
|
721
807
|
const char *thr = StringValueCStr(inspect);
|
722
808
|
|
723
809
|
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
@@ -781,10 +867,11 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
|
781
867
|
args.sql_len = RSTRING_LEN(args.sql);
|
782
868
|
args.wrapper = wrapper;
|
783
869
|
|
784
|
-
|
870
|
+
rb_mysql_client_set_active_fiber(self);
|
785
871
|
|
786
872
|
#ifndef _WIN32
|
787
873
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
874
|
+
(void)RB_GC_GUARD(sql);
|
788
875
|
|
789
876
|
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
790
877
|
return Qnil;
|
@@ -797,7 +884,8 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
|
797
884
|
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
798
885
|
}
|
799
886
|
#else
|
800
|
-
do_send_query(&args);
|
887
|
+
do_send_query((VALUE)&args);
|
888
|
+
(void)RB_GC_GUARD(sql);
|
801
889
|
|
802
890
|
/* this will just block until the result is ready */
|
803
891
|
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
@@ -908,10 +996,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
908
996
|
retval = charval;
|
909
997
|
break;
|
910
998
|
|
999
|
+
#ifdef HAVE_MYSQL_DEFAULT_AUTH
|
911
1000
|
case MYSQL_DEFAULT_AUTH:
|
912
1001
|
charval = (const char *)StringValueCStr(value);
|
913
1002
|
retval = charval;
|
914
1003
|
break;
|
1004
|
+
#endif
|
915
1005
|
|
916
1006
|
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
917
1007
|
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
@@ -1021,6 +1111,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
|
|
1021
1111
|
return ULL2NUM(mysql_insert_id(wrapper->client));
|
1022
1112
|
}
|
1023
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
|
+
|
1024
1144
|
/* call-seq:
|
1025
1145
|
* client.affected_rows
|
1026
1146
|
*
|
@@ -1315,12 +1435,31 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1315
1435
|
static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE capath, VALUE cipher) {
|
1316
1436
|
GET_CLIENT(self);
|
1317
1437
|
|
1438
|
+
#ifdef HAVE_MYSQL_SSL_SET
|
1318
1439
|
mysql_ssl_set(wrapper->client,
|
1319
1440
|
NIL_P(key) ? NULL : StringValueCStr(key),
|
1320
1441
|
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1321
1442
|
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1322
1443
|
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1323
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
|
1324
1463
|
|
1325
1464
|
return self;
|
1326
1465
|
}
|
@@ -1347,7 +1486,11 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1347
1486
|
}
|
1348
1487
|
|
1349
1488
|
static VALUE set_default_auth(VALUE self, VALUE value) {
|
1489
|
+
#ifdef HAVE_MYSQL_DEFAULT_AUTH
|
1350
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
|
1351
1494
|
}
|
1352
1495
|
|
1353
1496
|
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
@@ -1411,6 +1554,7 @@ void init_mysql2_client() {
|
|
1411
1554
|
mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
|
1412
1555
|
#endif
|
1413
1556
|
cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
1557
|
+
rb_global_variable(&cMysql2Client);
|
1414
1558
|
|
1415
1559
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1416
1560
|
|
@@ -1441,6 +1585,7 @@ void init_mysql2_client() {
|
|
1441
1585
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1442
1586
|
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1443
1587
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1588
|
+
rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
|
1444
1589
|
|
1445
1590
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1446
1591
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1613,16 +1758,32 @@ void init_mysql2_client() {
|
|
1613
1758
|
INT2NUM(0));
|
1614
1759
|
#endif
|
1615
1760
|
|
1616
|
-
#
|
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
|
1617
1773
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1618
1774
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1619
1775
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1620
1776
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1621
1777
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1622
|
-
#
|
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
|
1623
1783
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1624
1784
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1625
1785
|
#endif
|
1786
|
+
#endif
|
1626
1787
|
|
1627
1788
|
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1628
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);
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
require 'English'
|
3
3
|
|
4
|
+
### Some helper functions
|
5
|
+
|
4
6
|
def asplode(lib)
|
5
7
|
if RUBY_PLATFORM =~ /mingw|mswin/
|
6
8
|
abort "-----\n#{lib} is missing. Check your installation of MySQL or Connector/C, and try again.\n-----"
|
@@ -15,19 +17,57 @@ def add_ssl_defines(header)
|
|
15
17
|
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|
|
16
18
|
m && have_const(ssl_mode, header)
|
17
19
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
if all_modes_found
|
21
|
+
$CFLAGS << ' -DFULL_SSL_MODE_SUPPORT'
|
22
|
+
else
|
23
|
+
# if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
|
24
|
+
# and the verify server cert option. This is also the case for MariaDB.
|
25
|
+
has_verify_support = have_const('MYSQL_OPT_SSL_VERIFY_SERVER_CERT', header)
|
26
|
+
has_enforce_support = have_const('MYSQL_OPT_SSL_ENFORCE', header)
|
27
|
+
$CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if !has_verify_support && !has_enforce_support
|
28
|
+
end
|
22
29
|
end
|
23
30
|
|
31
|
+
### Check for Ruby C extention interfaces
|
32
|
+
|
24
33
|
# 2.1+
|
25
34
|
have_func('rb_absint_size')
|
26
35
|
have_func('rb_absint_singlebit_p')
|
27
36
|
|
37
|
+
# 2.7+
|
38
|
+
have_func('rb_gc_mark_movable')
|
39
|
+
|
28
40
|
# Missing in RBX (https://github.com/rubinius/rubinius/issues/3771)
|
29
41
|
have_func('rb_wait_for_single_fd')
|
30
42
|
|
43
|
+
# 3.0+
|
44
|
+
have_func('rb_enc_interned_str', 'ruby.h')
|
45
|
+
|
46
|
+
### Find OpenSSL library
|
47
|
+
|
48
|
+
# User-specified OpenSSL if explicitly specified
|
49
|
+
if with_config('openssl-dir')
|
50
|
+
_, lib = dir_config('openssl')
|
51
|
+
if lib
|
52
|
+
# Ruby versions below 2.0 on Unix and below 2.1 on Windows
|
53
|
+
# do not properly search for lib directories, and must be corrected:
|
54
|
+
# https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
|
55
|
+
unless lib && lib[-3, 3] == 'lib'
|
56
|
+
@libdir_basename = 'lib'
|
57
|
+
_, lib = dir_config('openssl')
|
58
|
+
end
|
59
|
+
abort "-----\nCannot find library dir(s) #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
|
60
|
+
warn "-----\nUsing --with-openssl-dir=#{File.dirname lib}\n-----"
|
61
|
+
$LDFLAGS << " -L#{lib}"
|
62
|
+
end
|
63
|
+
# Homebrew OpenSSL on MacOS
|
64
|
+
elsif RUBY_PLATFORM =~ /darwin/ && system('command -v brew')
|
65
|
+
openssl_location = `brew --prefix openssl`.strip
|
66
|
+
$LDFLAGS << " -L#{openssl_location}/lib" if openssl_location
|
67
|
+
end
|
68
|
+
|
69
|
+
### Find MySQL client library
|
70
|
+
|
31
71
|
# borrowed from mysqlplus
|
32
72
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
33
73
|
dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
|
@@ -35,6 +75,7 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
|
|
35
75
|
/opt/local
|
36
76
|
/opt/local/mysql
|
37
77
|
/opt/local/lib/mysql5*
|
78
|
+
/opt/homebrew/opt/mysql*
|
38
79
|
/usr
|
39
80
|
/usr/mysql
|
40
81
|
/usr/local
|
@@ -42,6 +83,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w[
|
|
42
83
|
/usr/local/mysql-*
|
43
84
|
/usr/local/lib/mysql5*
|
44
85
|
/usr/local/opt/mysql5*
|
86
|
+
/usr/local/opt/mysql@*
|
87
|
+
/usr/local/opt/mysql-client
|
88
|
+
/usr/local/opt/mysql-client@*
|
45
89
|
].map { |dir| dir << '/bin' }
|
46
90
|
|
47
91
|
# For those without HOMEBREW_ROOT in PATH
|
@@ -108,6 +152,7 @@ have_struct_member('MYSQL', 'net.vio', mysql_h)
|
|
108
152
|
have_struct_member('MYSQL', 'net.pvio', mysql_h)
|
109
153
|
|
110
154
|
# These constants are actually enums, so they cannot be detected by #ifdef in C code.
|
155
|
+
have_const('MYSQL_DEFAULT_AUTH', mysql_h)
|
111
156
|
have_const('MYSQL_ENABLE_CLEARTEXT_PLUGIN', mysql_h)
|
112
157
|
have_const('SERVER_QUERY_NO_GOOD_INDEX_USED', mysql_h)
|
113
158
|
have_const('SERVER_QUERY_NO_INDEX_USED', mysql_h)
|
@@ -119,10 +164,16 @@ have_const('MYSQL_OPTION_MULTI_STATEMENTS_OFF', mysql_h)
|
|
119
164
|
# to retain compatibility with the typedef in earlier MySQLs.
|
120
165
|
have_type('my_bool', mysql_h)
|
121
166
|
|
167
|
+
# detect mysql functions
|
168
|
+
have_func('mysql_ssl_set', mysql_h)
|
169
|
+
|
170
|
+
### Compiler flags to help catch errors
|
171
|
+
|
122
172
|
# This is our wishlist. We use whichever flags work on the host.
|
123
173
|
# -Wall and -Wextra are included by default.
|
124
174
|
wishlist = [
|
125
175
|
'-Weverything',
|
176
|
+
'-Wno-compound-token-split-by-macro', # Fixed in Ruby 2.7+ at https://bugs.ruby-lang.org/issues/17865
|
126
177
|
'-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
|
127
178
|
'-Wno-conditional-uninitialized', # false positive in client.c
|
128
179
|
'-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
|
@@ -147,8 +198,10 @@ end
|
|
147
198
|
|
148
199
|
$CFLAGS << ' ' << usable_flags.join(' ')
|
149
200
|
|
201
|
+
### Sanitizers to help with debugging -- many are available on both Clang/LLVM and GCC
|
202
|
+
|
150
203
|
enabled_sanitizers = disabled_sanitizers = []
|
151
|
-
# Specify a
|
204
|
+
# Specify a comma-separated list of sanitizers, or try them all by default
|
152
205
|
sanitizers = with_config('sanitize')
|
153
206
|
case sanitizers
|
154
207
|
when true
|
@@ -164,7 +217,7 @@ when String
|
|
164
217
|
end
|
165
218
|
end
|
166
219
|
|
167
|
-
unless disabled_sanitizers.empty?
|
220
|
+
unless disabled_sanitizers.empty? # rubocop:disable Style/IfUnlessModifier
|
168
221
|
abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
|
169
222
|
end
|
170
223
|
|
@@ -181,6 +234,8 @@ unless enabled_sanitizers.empty?
|
|
181
234
|
$CFLAGS << ' -g -fno-omit-frame-pointer'
|
182
235
|
end
|
183
236
|
|
237
|
+
### Find MySQL Client on Windows, set RPATH to find the library at runtime
|
238
|
+
|
184
239
|
if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
|
185
240
|
# Build libmysql.a interface link library
|
186
241
|
require 'rake'
|