mysql2 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +107 -22
- data/ext/mysql2/client.c +195 -53
- data/ext/mysql2/client.h +9 -2
- data/ext/mysql2/extconf.rb +58 -6
- data/ext/mysql2/mysql2_ext.c +6 -1
- data/ext/mysql2/mysql2_ext.h +13 -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 +1 -1
- metadata +10 -3
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
|
*
|
@@ -1347,7 +1467,11 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1347
1467
|
}
|
1348
1468
|
|
1349
1469
|
static VALUE set_default_auth(VALUE self, VALUE value) {
|
1470
|
+
#ifdef HAVE_MYSQL_DEFAULT_AUTH
|
1350
1471
|
return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
|
1472
|
+
#else
|
1473
|
+
rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
|
1474
|
+
#endif
|
1351
1475
|
}
|
1352
1476
|
|
1353
1477
|
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
@@ -1411,6 +1535,7 @@ void init_mysql2_client() {
|
|
1411
1535
|
mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
|
1412
1536
|
#endif
|
1413
1537
|
cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
1538
|
+
rb_global_variable(&cMysql2Client);
|
1414
1539
|
|
1415
1540
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1416
1541
|
|
@@ -1441,6 +1566,7 @@ void init_mysql2_client() {
|
|
1441
1566
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1442
1567
|
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1443
1568
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1569
|
+
rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
|
1444
1570
|
|
1445
1571
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1446
1572
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1613,16 +1739,32 @@ void init_mysql2_client() {
|
|
1613
1739
|
INT2NUM(0));
|
1614
1740
|
#endif
|
1615
1741
|
|
1616
|
-
#
|
1742
|
+
#ifdef CLIENT_SESSION_TRACK
|
1743
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
|
1744
|
+
/* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
|
1745
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
|
1746
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
|
1747
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
|
1748
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
|
1749
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
|
1750
|
+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
|
1751
|
+
#endif
|
1752
|
+
|
1753
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.6.36 and MySQL 5.7.11 and above
|
1617
1754
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1618
1755
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1619
1756
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1620
1757
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1621
1758
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1622
|
-
#
|
1759
|
+
#else
|
1760
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
|
1761
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1762
|
+
#endif
|
1763
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
|
1623
1764
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1624
1765
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1625
1766
|
#endif
|
1767
|
+
#endif
|
1626
1768
|
|
1627
1769
|
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1628
1770
|
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,13 @@ 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
|
+
### Compiler flags to help catch errors
|
168
|
+
|
122
169
|
# This is our wishlist. We use whichever flags work on the host.
|
123
170
|
# -Wall and -Wextra are included by default.
|
124
171
|
wishlist = [
|
125
172
|
'-Weverything',
|
173
|
+
'-Wno-compound-token-split-by-macro', # Fixed in Ruby 2.7+ at https://bugs.ruby-lang.org/issues/17865
|
126
174
|
'-Wno-bad-function-cast', # rb_thread_call_without_gvl returns void * that we cast to VALUE
|
127
175
|
'-Wno-conditional-uninitialized', # false positive in client.c
|
128
176
|
'-Wno-covered-switch-default', # result.c -- enum_field_types (when fully covered, e.g. mysql 5.5)
|
@@ -147,8 +195,10 @@ end
|
|
147
195
|
|
148
196
|
$CFLAGS << ' ' << usable_flags.join(' ')
|
149
197
|
|
198
|
+
### Sanitizers to help with debugging -- many are available on both Clang/LLVM and GCC
|
199
|
+
|
150
200
|
enabled_sanitizers = disabled_sanitizers = []
|
151
|
-
# Specify a
|
201
|
+
# Specify a comma-separated list of sanitizers, or try them all by default
|
152
202
|
sanitizers = with_config('sanitize')
|
153
203
|
case sanitizers
|
154
204
|
when true
|
@@ -164,7 +214,7 @@ when String
|
|
164
214
|
end
|
165
215
|
end
|
166
216
|
|
167
|
-
unless disabled_sanitizers.empty?
|
217
|
+
unless disabled_sanitizers.empty? # rubocop:disable Style/IfUnlessModifier
|
168
218
|
abort "-----\nCould not enable requested sanitizers: #{disabled_sanitizers.join(',')}\n-----"
|
169
219
|
end
|
170
220
|
|
@@ -181,6 +231,8 @@ unless enabled_sanitizers.empty?
|
|
181
231
|
$CFLAGS << ' -g -fno-omit-frame-pointer'
|
182
232
|
end
|
183
233
|
|
234
|
+
### Find MySQL Client on Windows, set RPATH to find the library at runtime
|
235
|
+
|
184
236
|
if RUBY_PLATFORM =~ /mswin|mingw/ && !defined?(RubyInstaller)
|
185
237
|
# Build libmysql.a interface link library
|
186
238
|
require 'rake'
|
data/ext/mysql2/mysql2_ext.c
CHANGED
@@ -4,9 +4,14 @@ VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
|
|
4
4
|
|
5
5
|
/* Ruby Extension initializer */
|
6
6
|
void Init_mysql2() {
|
7
|
-
mMysql2
|
7
|
+
mMysql2 = rb_define_module("Mysql2");
|
8
|
+
rb_global_variable(&mMysql2);
|
9
|
+
|
8
10
|
cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
|
11
|
+
rb_global_variable(&cMysql2Error);
|
12
|
+
|
9
13
|
cMysql2TimeoutError = rb_const_get(cMysql2Error, rb_intern("TimeoutError"));
|
14
|
+
rb_global_variable(&cMysql2TimeoutError);
|
10
15
|
|
11
16
|
init_mysql2_client();
|
12
17
|
init_mysql2_result();
|
data/ext/mysql2/mysql2_ext.h
CHANGED
@@ -36,6 +36,19 @@ void Init_mysql2(void);
|
|
36
36
|
typedef bool my_bool;
|
37
37
|
#endif
|
38
38
|
|
39
|
+
// ruby 2.7+
|
40
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
41
|
+
#define rb_mysql2_gc_location(ptr) ptr = rb_gc_location(ptr)
|
42
|
+
#else
|
43
|
+
#define rb_gc_mark_movable(ptr) rb_gc_mark(ptr)
|
44
|
+
#define rb_mysql2_gc_location(ptr)
|
45
|
+
#endif
|
46
|
+
|
47
|
+
// ruby 2.2+
|
48
|
+
#ifdef TypedData_Make_Struct
|
49
|
+
#define NEW_TYPEDDATA_WRAPPER 1
|
50
|
+
#endif
|
51
|
+
|
39
52
|
#include <client.h>
|
40
53
|
#include <statement.h>
|
41
54
|
#include <result.h>
|