mysql2 0.4.4 → 0.5.0
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 +90 -52
- data/examples/eventmachine.rb +0 -2
- data/examples/threaded.rb +2 -4
- data/ext/mysql2/client.c +260 -76
- data/ext/mysql2/client.h +2 -47
- data/ext/mysql2/extconf.rb +44 -18
- data/ext/mysql2/mysql2_ext.c +2 -1
- data/ext/mysql2/mysql2_ext.h +8 -8
- data/ext/mysql2/result.c +23 -74
- data/ext/mysql2/statement.c +139 -63
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +50 -31
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +49 -20
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -9
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +14 -15
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +6 -6
- data/spec/mysql2/client_spec.rb +318 -237
- data/spec/mysql2/error_spec.rb +4 -10
- data/spec/mysql2/result_spec.rb +124 -158
- data/spec/mysql2/statement_spec.rb +185 -180
- data/spec/spec_helper.rb +79 -61
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +2 -2
- data/support/ruby_enc_to_mysql.rb +5 -5
- metadata +16 -14
data/ext/mysql2/client.c
CHANGED
@@ -15,24 +15,31 @@
|
|
15
15
|
#include "mysql_enc_name_to_ruby.h"
|
16
16
|
|
17
17
|
VALUE cMysql2Client;
|
18
|
-
extern VALUE mMysql2, cMysql2Error;
|
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
|
+
static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
|
20
21
|
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
|
21
22
|
|
22
|
-
#ifndef HAVE_RB_HASH_DUP
|
23
|
-
VALUE rb_hash_dup(VALUE other) {
|
24
|
-
return rb_funcall(rb_cHash, intern_brackets, 1, other);
|
25
|
-
}
|
26
|
-
#endif
|
27
|
-
|
28
23
|
#define REQUIRE_INITIALIZED(wrapper) \
|
29
24
|
if (!wrapper->initialized) { \
|
30
25
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
26
|
}
|
32
27
|
|
28
|
+
#if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
|
29
|
+
#define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
|
30
|
+
#elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
|
31
|
+
#define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
|
32
|
+
#endif
|
33
|
+
|
34
|
+
#define REQUIRE_CONNECTED(wrapper) \
|
35
|
+
REQUIRE_INITIALIZED(wrapper) \
|
36
|
+
if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
|
37
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
38
|
+
}
|
39
|
+
|
33
40
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
34
41
|
REQUIRE_INITIALIZED(wrapper) \
|
35
|
-
if (wrapper
|
42
|
+
if (CONNECTED(wrapper)) { \
|
36
43
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
37
44
|
}
|
38
45
|
|
@@ -41,12 +48,24 @@ VALUE rb_hash_dup(VALUE other) {
|
|
41
48
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
42
49
|
* linking against the server itself
|
43
50
|
*/
|
44
|
-
#
|
51
|
+
#if defined(MARIADB_CLIENT_VERSION_STR)
|
52
|
+
#define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
|
53
|
+
#elif defined(LIBMYSQL_VERSION)
|
45
54
|
#define MYSQL_LINK_VERSION LIBMYSQL_VERSION
|
46
55
|
#else
|
47
56
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
48
57
|
#endif
|
49
58
|
|
59
|
+
/*
|
60
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
61
|
+
*/
|
62
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
63
|
+
#define SSL_MODE_DISABLED 1
|
64
|
+
#define SSL_MODE_REQUIRED 3
|
65
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
66
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
67
|
+
#endif
|
68
|
+
|
50
69
|
/*
|
51
70
|
* used to pass all arguments to mysql_real_connect while inside
|
52
71
|
* rb_thread_call_without_gvl
|
@@ -83,6 +102,42 @@ struct nogvl_select_db_args {
|
|
83
102
|
char *db;
|
84
103
|
};
|
85
104
|
|
105
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
106
|
+
unsigned long version = mysql_get_client_version();
|
107
|
+
|
108
|
+
if (version < 50703) {
|
109
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
110
|
+
return Qnil;
|
111
|
+
}
|
112
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
113
|
+
GET_CLIENT(self);
|
114
|
+
int val = NUM2INT( setting );
|
115
|
+
if (version >= 50703 && version < 50711) {
|
116
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
117
|
+
my_bool b = ( val == SSL_MODE_REQUIRED );
|
118
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
119
|
+
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
|
+
}
|
124
|
+
}
|
125
|
+
#endif
|
126
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
127
|
+
GET_CLIENT(self);
|
128
|
+
int val = NUM2INT( setting );
|
129
|
+
|
130
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
131
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
132
|
+
}
|
133
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
134
|
+
|
135
|
+
return INT2NUM(result);
|
136
|
+
#endif
|
137
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
138
|
+
return Qnil;
|
139
|
+
#endif
|
140
|
+
}
|
86
141
|
/*
|
87
142
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
88
143
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -118,10 +173,8 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
118
173
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
|
119
174
|
VALUE e;
|
120
175
|
|
121
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
122
176
|
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
123
177
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
124
|
-
#endif
|
125
178
|
|
126
179
|
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
127
180
|
rb_error_msg,
|
@@ -211,11 +264,10 @@ static VALUE invalidate_fd(int clientfd)
|
|
211
264
|
static void *nogvl_close(void *ptr) {
|
212
265
|
mysql_client_wrapper *wrapper = ptr;
|
213
266
|
|
214
|
-
if (wrapper->
|
267
|
+
if (!wrapper->closed) {
|
215
268
|
mysql_close(wrapper->client);
|
216
|
-
|
217
|
-
wrapper->
|
218
|
-
wrapper->connected = 0;
|
269
|
+
wrapper->closed = 1;
|
270
|
+
wrapper->reconnect_enabled = 0;
|
219
271
|
wrapper->active_thread = Qnil;
|
220
272
|
}
|
221
273
|
|
@@ -234,7 +286,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
234
286
|
|
235
287
|
if (wrapper->refcount == 0) {
|
236
288
|
#ifndef _WIN32
|
237
|
-
if (wrapper
|
289
|
+
if (CONNECTED(wrapper) && !wrapper->automatic_close) {
|
238
290
|
/* The client is being garbage collected while connected. Prevent
|
239
291
|
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
240
292
|
* the socket by invalidating it. invalidate_fd() will drop this
|
@@ -246,10 +298,12 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
246
298
|
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
247
299
|
close(wrapper->client->net.fd);
|
248
300
|
}
|
301
|
+
wrapper->client->net.fd = -1;
|
249
302
|
}
|
250
303
|
#endif
|
251
304
|
|
252
305
|
nogvl_close(wrapper);
|
306
|
+
xfree(wrapper->client);
|
253
307
|
xfree(wrapper);
|
254
308
|
}
|
255
309
|
}
|
@@ -264,9 +318,9 @@ static VALUE allocate(VALUE klass) {
|
|
264
318
|
wrapper->server_version = 0;
|
265
319
|
wrapper->reconnect_enabled = 0;
|
266
320
|
wrapper->connect_timeout = 0;
|
267
|
-
wrapper->connected = 0; /* means that a database connection is open */
|
268
321
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
269
322
|
wrapper->refcount = 1;
|
323
|
+
wrapper->closed = 0;
|
270
324
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
271
325
|
|
272
326
|
return obj;
|
@@ -296,9 +350,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
296
350
|
return str;
|
297
351
|
} else {
|
298
352
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
299
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
300
353
|
rb_enc_copy(rb_str, str);
|
301
|
-
#endif
|
302
354
|
xfree(newStr);
|
303
355
|
return rb_str;
|
304
356
|
}
|
@@ -325,9 +377,7 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
325
377
|
}
|
326
378
|
|
327
379
|
rb_str = rb_str_new2(info);
|
328
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
329
380
|
rb_enc_associate(rb_str, rb_utf8_encoding());
|
330
|
-
#endif
|
331
381
|
|
332
382
|
return rb_str;
|
333
383
|
}
|
@@ -345,14 +395,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
|
345
395
|
}
|
346
396
|
|
347
397
|
rb_str = rb_str_new2(cipher);
|
348
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
349
398
|
rb_enc_associate(rb_str, rb_utf8_encoding());
|
350
|
-
#endif
|
351
399
|
|
352
400
|
return rb_str;
|
353
401
|
}
|
354
402
|
|
355
|
-
|
403
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
404
|
+
static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
|
405
|
+
{
|
406
|
+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
|
407
|
+
rb_encoding *enc = rb_to_encoding(wrapper->encoding);
|
408
|
+
key = rb_str_export_to_enc(key, enc);
|
409
|
+
value = rb_str_export_to_enc(value, enc);
|
410
|
+
|
411
|
+
mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
|
412
|
+
return ST_CONTINUE;
|
413
|
+
}
|
414
|
+
#endif
|
415
|
+
|
416
|
+
static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
|
356
417
|
struct nogvl_connect_args args;
|
357
418
|
time_t start_time, end_time, elapsed_time, connect_timeout;
|
358
419
|
VALUE rv;
|
@@ -367,6 +428,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
367
428
|
args.mysql = wrapper->client;
|
368
429
|
args.client_flag = NUM2ULONG(flags);
|
369
430
|
|
431
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
432
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
|
433
|
+
rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
|
434
|
+
#endif
|
435
|
+
|
370
436
|
if (wrapper->connect_timeout)
|
371
437
|
time(&start_time);
|
372
438
|
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
@@ -397,7 +463,6 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
397
463
|
}
|
398
464
|
|
399
465
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
400
|
-
wrapper->connected = 1;
|
401
466
|
return self;
|
402
467
|
}
|
403
468
|
|
@@ -412,13 +477,23 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
412
477
|
static VALUE rb_mysql_client_close(VALUE self) {
|
413
478
|
GET_CLIENT(self);
|
414
479
|
|
415
|
-
if (wrapper->
|
480
|
+
if (wrapper->client) {
|
416
481
|
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
417
482
|
}
|
418
483
|
|
419
484
|
return Qnil;
|
420
485
|
}
|
421
486
|
|
487
|
+
/* call-seq:
|
488
|
+
* client.closed?
|
489
|
+
*
|
490
|
+
* @return [Boolean]
|
491
|
+
*/
|
492
|
+
static VALUE rb_mysql_client_closed(VALUE self) {
|
493
|
+
GET_CLIENT(self);
|
494
|
+
return CONNECTED(wrapper) ? Qfalse : Qtrue;
|
495
|
+
}
|
496
|
+
|
422
497
|
/*
|
423
498
|
* mysql_send_query is unlikely to block since most queries are small
|
424
499
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
@@ -520,11 +595,14 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
520
595
|
return Qnil;
|
521
596
|
}
|
522
597
|
|
598
|
+
// Duplicate the options hash and put the copy in the Result object
|
523
599
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
524
600
|
(void)RB_GC_GUARD(current);
|
525
601
|
Check_Type(current, T_HASH);
|
526
602
|
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
527
603
|
|
604
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
605
|
+
|
528
606
|
return resultObj;
|
529
607
|
}
|
530
608
|
|
@@ -538,14 +616,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
538
616
|
GET_CLIENT(self);
|
539
617
|
|
540
618
|
wrapper->active_thread = Qnil;
|
541
|
-
wrapper->connected = 0;
|
542
619
|
|
543
620
|
/* Invalidate the MySQL socket to prevent further communication.
|
544
621
|
* The GC will come along later and call mysql_close to free it.
|
545
622
|
*/
|
546
|
-
if (
|
547
|
-
|
548
|
-
|
623
|
+
if (CONNECTED(wrapper)) {
|
624
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
625
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
626
|
+
close(wrapper->client->net.fd);
|
627
|
+
}
|
628
|
+
wrapper->client->net.fd = -1;
|
549
629
|
}
|
550
630
|
|
551
631
|
rb_exc_raise(error);
|
@@ -580,7 +660,7 @@ static VALUE do_query(void *args) {
|
|
580
660
|
retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
|
581
661
|
|
582
662
|
if (retval == 0) {
|
583
|
-
rb_raise(
|
663
|
+
rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
|
584
664
|
}
|
585
665
|
|
586
666
|
if (retval < 0) {
|
@@ -594,26 +674,32 @@ static VALUE do_query(void *args) {
|
|
594
674
|
|
595
675
|
return Qnil;
|
596
676
|
}
|
597
|
-
#
|
598
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
599
|
-
VALUE self = args;
|
600
|
-
MYSQL_RES *result;
|
677
|
+
#endif
|
601
678
|
|
679
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
602
680
|
GET_CLIENT(self);
|
603
681
|
|
682
|
+
/* Check if execution terminated while result was still being read. */
|
604
683
|
if (!NIL_P(wrapper->active_thread)) {
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
684
|
+
if (CONNECTED(wrapper)) {
|
685
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
686
|
+
#ifndef _WIN32
|
687
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
688
|
+
rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
|
689
|
+
close(wrapper->client->net.fd);
|
690
|
+
}
|
691
|
+
#else
|
692
|
+
close(wrapper->client->net.fd);
|
693
|
+
#endif
|
694
|
+
wrapper->client->net.fd = -1;
|
695
|
+
}
|
696
|
+
/* Skip mysql client check performed before command execution. */
|
697
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
611
698
|
wrapper->active_thread = Qnil;
|
612
699
|
}
|
613
700
|
|
614
701
|
return Qnil;
|
615
702
|
}
|
616
|
-
#endif
|
617
703
|
|
618
704
|
void rb_mysql_client_set_active_thread(VALUE self) {
|
619
705
|
VALUE thread_current = rb_thread_current();
|
@@ -669,7 +755,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
669
755
|
* Query the database with +sql+, with optional +options+. For the possible
|
670
756
|
* options, see default_query_options on the Mysql2::Client class.
|
671
757
|
*/
|
672
|
-
static VALUE
|
758
|
+
static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
673
759
|
#ifndef _WIN32
|
674
760
|
struct async_query_args async_args;
|
675
761
|
#endif
|
@@ -684,12 +770,8 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
684
770
|
rb_iv_set(self, "@current_query_options", current);
|
685
771
|
|
686
772
|
Check_Type(sql, T_STRING);
|
687
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
688
773
|
/* ensure the string is in the encoding the connection is expecting */
|
689
774
|
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
690
|
-
#else
|
691
|
-
args.sql = sql;
|
692
|
-
#endif
|
693
775
|
args.sql_ptr = RSTRING_PTR(args.sql);
|
694
776
|
args.sql_len = RSTRING_LEN(args.sql);
|
695
777
|
args.wrapper = wrapper;
|
@@ -707,13 +789,13 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
707
789
|
|
708
790
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
709
791
|
|
710
|
-
return rb_mysql_client_async_result
|
792
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
711
793
|
}
|
712
794
|
#else
|
713
795
|
do_send_query(&args);
|
714
796
|
|
715
797
|
/* this will just block until the result is ready */
|
716
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
798
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
717
799
|
#endif
|
718
800
|
}
|
719
801
|
|
@@ -726,20 +808,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
726
808
|
unsigned char *newStr;
|
727
809
|
VALUE rb_str;
|
728
810
|
unsigned long newLen, oldLen;
|
729
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
730
811
|
rb_encoding *default_internal_enc;
|
731
812
|
rb_encoding *conn_enc;
|
732
|
-
#endif
|
733
813
|
GET_CLIENT(self);
|
734
814
|
|
735
815
|
REQUIRE_CONNECTED(wrapper);
|
736
816
|
Check_Type(str, T_STRING);
|
737
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
738
817
|
default_internal_enc = rb_default_internal_encoding();
|
739
818
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
740
819
|
/* ensure the string is in the encoding the connection is expecting */
|
741
820
|
str = rb_str_export_to_enc(str, conn_enc);
|
742
|
-
#endif
|
743
821
|
|
744
822
|
oldLen = RSTRING_LEN(str);
|
745
823
|
newStr = xmalloc(oldLen*2+1);
|
@@ -747,21 +825,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
747
825
|
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
748
826
|
if (newLen == oldLen) {
|
749
827
|
/* no need to return a new ruby string if nothing changed */
|
750
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
751
828
|
if (default_internal_enc) {
|
752
829
|
str = rb_str_export_to_enc(str, default_internal_enc);
|
753
830
|
}
|
754
|
-
#endif
|
755
831
|
xfree(newStr);
|
756
832
|
return str;
|
757
833
|
} else {
|
758
834
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
759
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
760
835
|
rb_enc_associate(rb_str, conn_enc);
|
761
836
|
if (default_internal_enc) {
|
762
837
|
rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
|
763
838
|
}
|
764
|
-
#endif
|
765
839
|
xfree(newStr);
|
766
840
|
return rb_str;
|
767
841
|
}
|
@@ -807,10 +881,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
807
881
|
retval = &boolval;
|
808
882
|
break;
|
809
883
|
|
884
|
+
#ifdef MYSQL_SECURE_AUTH
|
810
885
|
case MYSQL_SECURE_AUTH:
|
811
886
|
boolval = (value == Qfalse ? 0 : 1);
|
812
887
|
retval = &boolval;
|
813
888
|
break;
|
889
|
+
#endif
|
814
890
|
|
815
891
|
case MYSQL_READ_DEFAULT_FILE:
|
816
892
|
charval = (const char *)StringValueCStr(value);
|
@@ -827,6 +903,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
827
903
|
retval = charval;
|
828
904
|
break;
|
829
905
|
|
906
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
907
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
908
|
+
boolval = (value == Qfalse ? 0 : 1);
|
909
|
+
retval = &boolval;
|
910
|
+
break;
|
911
|
+
#endif
|
912
|
+
|
830
913
|
default:
|
831
914
|
return Qfalse;
|
832
915
|
}
|
@@ -863,10 +946,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
863
946
|
version = rb_str_new2(mysql_get_client_info());
|
864
947
|
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
865
948
|
|
866
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
867
949
|
rb_enc_associate(version, rb_usascii_encoding());
|
868
950
|
rb_enc_associate(header_version, rb_usascii_encoding());
|
869
|
-
#endif
|
870
951
|
|
871
952
|
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
872
953
|
rb_hash_aset(version_info, sym_version, version);
|
@@ -882,27 +963,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
882
963
|
*/
|
883
964
|
static VALUE rb_mysql_client_server_info(VALUE self) {
|
884
965
|
VALUE version, server_info;
|
885
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
886
966
|
rb_encoding *default_internal_enc;
|
887
967
|
rb_encoding *conn_enc;
|
888
|
-
#endif
|
889
968
|
GET_CLIENT(self);
|
890
969
|
|
891
970
|
REQUIRE_CONNECTED(wrapper);
|
892
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
893
971
|
default_internal_enc = rb_default_internal_encoding();
|
894
972
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
895
|
-
#endif
|
896
973
|
|
897
974
|
version = rb_hash_new();
|
898
975
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
|
899
976
|
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
|
900
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
901
977
|
rb_enc_associate(server_info, conn_enc);
|
902
978
|
if (default_internal_enc) {
|
903
979
|
server_info = rb_str_export_to_enc(server_info, default_internal_enc);
|
904
980
|
}
|
905
|
-
#endif
|
906
981
|
rb_hash_aset(version, sym_version, server_info);
|
907
982
|
return version;
|
908
983
|
}
|
@@ -1016,13 +1091,30 @@ static void *nogvl_ping(void *ptr) {
|
|
1016
1091
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
1017
1092
|
GET_CLIENT(self);
|
1018
1093
|
|
1019
|
-
if (!wrapper
|
1094
|
+
if (!CONNECTED(wrapper)) {
|
1020
1095
|
return Qfalse;
|
1021
1096
|
} else {
|
1022
1097
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
1023
1098
|
}
|
1024
1099
|
}
|
1025
1100
|
|
1101
|
+
/* call-seq:
|
1102
|
+
* client.set_server_option(value)
|
1103
|
+
*
|
1104
|
+
* Enables or disables an option for the connection.
|
1105
|
+
* Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
|
1106
|
+
* for more information.
|
1107
|
+
*/
|
1108
|
+
static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
|
1109
|
+
GET_CLIENT(self);
|
1110
|
+
|
1111
|
+
if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
|
1112
|
+
return Qtrue;
|
1113
|
+
} else {
|
1114
|
+
return Qfalse;
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
|
1026
1118
|
/* call-seq:
|
1027
1119
|
* client.more_results?
|
1028
1120
|
*
|
@@ -1081,6 +1173,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1081
1173
|
return Qnil;
|
1082
1174
|
}
|
1083
1175
|
|
1176
|
+
// Duplicate the options hash and put the copy in the Result object
|
1084
1177
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1085
1178
|
(void)RB_GC_GUARD(current);
|
1086
1179
|
Check_Type(current, T_HASH);
|
@@ -1089,7 +1182,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1089
1182
|
return resultObj;
|
1090
1183
|
}
|
1091
1184
|
|
1092
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1093
1185
|
/* call-seq:
|
1094
1186
|
* client.encoding
|
1095
1187
|
*
|
@@ -1099,7 +1191,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1099
1191
|
GET_CLIENT(self);
|
1100
1192
|
return wrapper->encoding;
|
1101
1193
|
}
|
1102
|
-
#endif
|
1103
1194
|
|
1104
1195
|
/* call-seq:
|
1105
1196
|
* client.automatic_close?
|
@@ -1185,16 +1276,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1185
1276
|
|
1186
1277
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1187
1278
|
char *charset_name;
|
1188
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1189
1279
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1190
1280
|
rb_encoding *enc;
|
1191
1281
|
VALUE rb_enc;
|
1192
|
-
#endif
|
1193
1282
|
GET_CLIENT(self);
|
1194
1283
|
|
1284
|
+
Check_Type(value, T_STRING);
|
1195
1285
|
charset_name = RSTRING_PTR(value);
|
1196
1286
|
|
1197
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1198
1287
|
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1199
1288
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1200
1289
|
VALUE inspect = rb_inspect(value);
|
@@ -1204,7 +1293,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1204
1293
|
rb_enc = rb_enc_from_encoding(enc);
|
1205
1294
|
wrapper->encoding = rb_enc;
|
1206
1295
|
}
|
1207
|
-
#endif
|
1208
1296
|
|
1209
1297
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
1210
1298
|
/* TODO: warning - unable to set charset */
|
@@ -1228,7 +1316,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1228
1316
|
}
|
1229
1317
|
|
1230
1318
|
static VALUE set_secure_auth(VALUE self, VALUE value) {
|
1319
|
+
/* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
|
1320
|
+
#ifdef MYSQL_SECURE_AUTH
|
1231
1321
|
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
|
1322
|
+
#else
|
1323
|
+
return Qfalse;
|
1324
|
+
#endif
|
1232
1325
|
}
|
1233
1326
|
|
1234
1327
|
static VALUE set_read_default_file(VALUE self, VALUE value) {
|
@@ -1243,6 +1336,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1243
1336
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1244
1337
|
}
|
1245
1338
|
|
1339
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1340
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1341
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1342
|
+
#else
|
1343
|
+
rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
|
1344
|
+
#endif
|
1345
|
+
}
|
1346
|
+
|
1246
1347
|
static VALUE initialize_ext(VALUE self) {
|
1247
1348
|
GET_CLIENT(self);
|
1248
1349
|
|
@@ -1303,6 +1404,7 @@ void init_mysql2_client() {
|
|
1303
1404
|
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1304
1405
|
|
1305
1406
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1407
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1306
1408
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1307
1409
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1308
1410
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
@@ -1314,6 +1416,7 @@ void init_mysql2_client() {
|
|
1314
1416
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1315
1417
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1316
1418
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1419
|
+
rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
|
1317
1420
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1318
1421
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1319
1422
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
@@ -1323,9 +1426,7 @@ void init_mysql2_client() {
|
|
1323
1426
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1324
1427
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1325
1428
|
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1326
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1327
1429
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1328
|
-
#endif
|
1329
1430
|
|
1330
1431
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1331
1432
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1337,9 +1438,11 @@ void init_mysql2_client() {
|
|
1337
1438
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1338
1439
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1339
1440
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1441
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1442
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1340
1443
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1341
|
-
rb_define_private_method(cMysql2Client, "connect",
|
1342
|
-
rb_define_private_method(cMysql2Client, "_query",
|
1444
|
+
rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
|
1445
|
+
rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
|
1343
1446
|
|
1344
1447
|
sym_id = ID2SYM(rb_intern("id"));
|
1345
1448
|
sym_version = ID2SYM(rb_intern("version"));
|
@@ -1350,6 +1453,10 @@ void init_mysql2_client() {
|
|
1350
1453
|
sym_array = ID2SYM(rb_intern("array"));
|
1351
1454
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1352
1455
|
|
1456
|
+
sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
|
1457
|
+
sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
|
1458
|
+
sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
|
1459
|
+
|
1353
1460
|
intern_brackets = rb_intern("[]");
|
1354
1461
|
intern_merge = rb_intern("merge");
|
1355
1462
|
intern_merge_bang = rb_intern("merge!");
|
@@ -1358,6 +1465,10 @@ void init_mysql2_client() {
|
|
1358
1465
|
#ifdef CLIENT_LONG_PASSWORD
|
1359
1466
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1360
1467
|
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1468
|
+
#else
|
1469
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1470
|
+
* but we're using it in our default connection flags. */
|
1471
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
1361
1472
|
#endif
|
1362
1473
|
|
1363
1474
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1435,6 +1546,16 @@ void init_mysql2_client() {
|
|
1435
1546
|
rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
|
1436
1547
|
#endif
|
1437
1548
|
|
1549
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
|
1550
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
|
1551
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
|
1552
|
+
#endif
|
1553
|
+
|
1554
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
1555
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
|
1556
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
|
1557
|
+
#endif
|
1558
|
+
|
1438
1559
|
#ifdef CLIENT_MULTI_STATEMENTS
|
1439
1560
|
rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
|
1440
1561
|
LONG2NUM(CLIENT_MULTI_STATEMENTS));
|
@@ -1464,4 +1585,67 @@ void init_mysql2_client() {
|
|
1464
1585
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1465
1586
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1466
1587
|
#endif
|
1588
|
+
|
1589
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
1590
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1591
|
+
LONG2NUM(CLIENT_CONNECT_ATTRS));
|
1592
|
+
#else
|
1593
|
+
/* HACK because MySQL 5.5 and earlier don't define this constant,
|
1594
|
+
* but we're using it in our default connection flags. */
|
1595
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1596
|
+
INT2NUM(0));
|
1597
|
+
#endif
|
1598
|
+
|
1599
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1600
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1601
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1602
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1603
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1604
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1605
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1606
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1607
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1608
|
+
#endif
|
1609
|
+
|
1610
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1611
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1612
|
+
#endif
|
1613
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1614
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1615
|
+
#endif
|
1616
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1617
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1618
|
+
#endif
|
1619
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1620
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1621
|
+
#endif
|
1622
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1623
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1624
|
+
#endif
|
1625
|
+
}
|
1626
|
+
|
1627
|
+
#define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
|
1628
|
+
|
1629
|
+
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
|
1630
|
+
VALUE server_flags = rb_hash_new();
|
1631
|
+
|
1632
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
|
1633
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
|
1634
|
+
#else
|
1635
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
|
1636
|
+
#endif
|
1637
|
+
|
1638
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
|
1639
|
+
rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
|
1640
|
+
#else
|
1641
|
+
rb_hash_aset(server_flags, sym_no_index_used, Qnil);
|
1642
|
+
#endif
|
1643
|
+
|
1644
|
+
#ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
|
1645
|
+
rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
|
1646
|
+
#else
|
1647
|
+
rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
|
1648
|
+
#endif
|
1649
|
+
|
1650
|
+
rb_iv_set(result, "@server_flags", server_flags);
|
1467
1651
|
}
|