mysql2 0.3.20 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1 -0
- data/README.md +183 -72
- data/ext/mysql2/client.c +432 -183
- data/ext/mysql2/client.h +10 -38
- data/ext/mysql2/extconf.rb +126 -56
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +3 -1
- data/ext/mysql2/mysql2_ext.h +13 -14
- data/ext/mysql2/mysql_enc_name_to_ruby.h +62 -58
- data/ext/mysql2/mysql_enc_to_ruby.h +82 -18
- data/ext/mysql2/result.c +505 -164
- data/ext/mysql2/result.h +11 -4
- data/ext/mysql2/statement.c +604 -0
- data/ext/mysql2/statement.h +17 -0
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +89 -28
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +6 -9
- data/lib/mysql2/error.rb +55 -35
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +11 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +46 -24
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +15 -11
- data/support/ruby_enc_to_mysql.rb +8 -6
- metadata +22 -77
- data/examples/eventmachine.rb +0 -21
- data/examples/threaded.rb +0 -20
- data/spec/configuration.yml.example +0 -17
- data/spec/em/em_spec.rb +0 -135
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -921
- data/spec/mysql2/error_spec.rb +0 -83
- data/spec/mysql2/result_spec.rb +0 -514
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -87
- data/spec/test_data +0 -1
data/ext/mysql2/client.c
CHANGED
@@ -15,51 +15,58 @@
|
|
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
|
21
|
-
|
22
|
-
|
23
|
-
static VALUE rb_hash_dup(VALUE other) {
|
24
|
-
return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
|
25
|
-
}
|
26
|
-
#endif
|
20
|
+
static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
|
21
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
|
22
|
+
intern_current_query_options, intern_read_timeout;
|
27
23
|
|
28
24
|
#define REQUIRE_INITIALIZED(wrapper) \
|
29
25
|
if (!wrapper->initialized) { \
|
30
26
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
27
|
}
|
32
28
|
|
29
|
+
#if defined(HAVE_MYSQL_NET_VIO) || defined(HAVE_ST_NET_VIO)
|
30
|
+
#define CONNECTED(wrapper) (wrapper->client->net.vio != NULL && wrapper->client->net.fd != -1)
|
31
|
+
#elif defined(HAVE_MYSQL_NET_PVIO) || defined(HAVE_ST_NET_PVIO)
|
32
|
+
#define CONNECTED(wrapper) (wrapper->client->net.pvio != NULL && wrapper->client->net.fd != -1)
|
33
|
+
#endif
|
34
|
+
|
33
35
|
#define REQUIRE_CONNECTED(wrapper) \
|
34
36
|
REQUIRE_INITIALIZED(wrapper) \
|
35
|
-
if (!wrapper
|
36
|
-
rb_raise(cMysql2Error, "
|
37
|
+
if (!CONNECTED(wrapper) && !wrapper->reconnect_enabled) { \
|
38
|
+
rb_raise(cMysql2Error, "MySQL client is not connected"); \
|
37
39
|
}
|
38
40
|
|
39
41
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
40
42
|
REQUIRE_INITIALIZED(wrapper) \
|
41
|
-
if (wrapper
|
43
|
+
if (CONNECTED(wrapper)) { \
|
42
44
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
43
45
|
}
|
44
46
|
|
45
|
-
#define MARK_CONN_INACTIVE(conn) \
|
46
|
-
wrapper->active_thread = Qnil;
|
47
|
-
|
48
|
-
#define GET_CLIENT(self) \
|
49
|
-
mysql_client_wrapper *wrapper; \
|
50
|
-
Data_Get_Struct(self, mysql_client_wrapper, wrapper)
|
51
|
-
|
52
47
|
/*
|
53
48
|
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
54
49
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
55
50
|
* linking against the server itself
|
56
51
|
*/
|
57
|
-
#
|
52
|
+
#if defined(MARIADB_CLIENT_VERSION_STR)
|
53
|
+
#define MYSQL_LINK_VERSION MARIADB_CLIENT_VERSION_STR
|
54
|
+
#elif defined(LIBMYSQL_VERSION)
|
58
55
|
#define MYSQL_LINK_VERSION LIBMYSQL_VERSION
|
59
56
|
#else
|
60
57
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
61
58
|
#endif
|
62
59
|
|
60
|
+
/*
|
61
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
62
|
+
*/
|
63
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
64
|
+
#define SSL_MODE_DISABLED 1
|
65
|
+
#define SSL_MODE_REQUIRED 3
|
66
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
67
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
68
|
+
#endif
|
69
|
+
|
63
70
|
/*
|
64
71
|
* used to pass all arguments to mysql_real_connect while inside
|
65
72
|
* rb_thread_call_without_gvl
|
@@ -96,6 +103,46 @@ struct nogvl_select_db_args {
|
|
96
103
|
char *db;
|
97
104
|
};
|
98
105
|
|
106
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
107
|
+
unsigned long version = mysql_get_client_version();
|
108
|
+
|
109
|
+
if (version < 50703) {
|
110
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
111
|
+
return Qnil;
|
112
|
+
}
|
113
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
114
|
+
GET_CLIENT(self);
|
115
|
+
int val = NUM2INT( setting );
|
116
|
+
// Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
|
117
|
+
if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
|
118
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
119
|
+
my_bool b = ( val == SSL_MODE_REQUIRED );
|
120
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
121
|
+
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
|
+
}
|
126
|
+
} else {
|
127
|
+
rb_warn( "Your mysql client library does not support ssl_mode as expected." );
|
128
|
+
return Qnil;
|
129
|
+
}
|
130
|
+
#endif
|
131
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
132
|
+
GET_CLIENT(self);
|
133
|
+
int val = NUM2INT( setting );
|
134
|
+
|
135
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
136
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
137
|
+
}
|
138
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
139
|
+
|
140
|
+
return INT2NUM(result);
|
141
|
+
#endif
|
142
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
143
|
+
return Qnil;
|
144
|
+
#endif
|
145
|
+
}
|
99
146
|
/*
|
100
147
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
101
148
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -131,21 +178,20 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
131
178
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
|
132
179
|
VALUE e;
|
133
180
|
|
134
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
135
181
|
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
136
182
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
137
|
-
#endif
|
138
183
|
|
139
|
-
e = rb_funcall(cMysql2Error,
|
140
|
-
|
141
|
-
|
184
|
+
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
185
|
+
rb_error_msg,
|
186
|
+
LONG2FIX(wrapper->server_version),
|
187
|
+
UINT2NUM(mysql_errno(wrapper->client)),
|
188
|
+
rb_sql_state);
|
142
189
|
rb_exc_raise(e);
|
143
|
-
return Qnil;
|
144
190
|
}
|
145
191
|
|
146
192
|
static void *nogvl_init(void *ptr) {
|
147
193
|
MYSQL *client;
|
148
|
-
mysql_client_wrapper *wrapper =
|
194
|
+
mysql_client_wrapper *wrapper = ptr;
|
149
195
|
|
150
196
|
/* may initialize embedded server and read /etc/services off disk */
|
151
197
|
client = mysql_init(wrapper->client);
|
@@ -221,41 +267,46 @@ static VALUE invalidate_fd(int clientfd)
|
|
221
267
|
#endif /* _WIN32 */
|
222
268
|
|
223
269
|
static void *nogvl_close(void *ptr) {
|
224
|
-
mysql_client_wrapper *wrapper;
|
225
|
-
wrapper = ptr;
|
226
|
-
if (wrapper->connected) {
|
227
|
-
wrapper->active_thread = Qnil;
|
228
|
-
wrapper->connected = 0;
|
229
|
-
#ifndef _WIN32
|
230
|
-
/* Invalidate the socket before calling mysql_close(). This prevents
|
231
|
-
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
232
|
-
* the socket. The difference is that invalidate_fd will drop this
|
233
|
-
* process's reference to the socket only, while a QUIT or shutdown()
|
234
|
-
* would render the underlying connection unusable, interrupting other
|
235
|
-
* processes which share this object across a fork().
|
236
|
-
*/
|
237
|
-
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
238
|
-
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
|
239
|
-
close(wrapper->client->net.fd);
|
240
|
-
return NULL;
|
241
|
-
}
|
242
|
-
#endif
|
270
|
+
mysql_client_wrapper *wrapper = ptr;
|
243
271
|
|
244
|
-
|
272
|
+
if (!wrapper->closed) {
|
273
|
+
mysql_close(wrapper->client);
|
274
|
+
wrapper->closed = 1;
|
275
|
+
wrapper->reconnect_enabled = 0;
|
276
|
+
wrapper->active_thread = Qnil;
|
245
277
|
}
|
246
278
|
|
247
279
|
return NULL;
|
248
280
|
}
|
249
281
|
|
282
|
+
/* this is called during GC */
|
250
283
|
static void rb_mysql_client_free(void *ptr) {
|
251
|
-
mysql_client_wrapper *wrapper =
|
284
|
+
mysql_client_wrapper *wrapper = ptr;
|
252
285
|
decr_mysql2_client(wrapper);
|
253
286
|
}
|
254
287
|
|
255
288
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
256
289
|
{
|
257
290
|
wrapper->refcount--;
|
291
|
+
|
258
292
|
if (wrapper->refcount == 0) {
|
293
|
+
#ifndef _WIN32
|
294
|
+
if (CONNECTED(wrapper) && !wrapper->automatic_close) {
|
295
|
+
/* The client is being garbage collected while connected. Prevent
|
296
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
297
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
298
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
299
|
+
* would render the underlying connection unusable, interrupting other
|
300
|
+
* processes which share this object across a fork().
|
301
|
+
*/
|
302
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
303
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
304
|
+
close(wrapper->client->net.fd);
|
305
|
+
}
|
306
|
+
wrapper->client->net.fd = -1;
|
307
|
+
}
|
308
|
+
#endif
|
309
|
+
|
259
310
|
nogvl_close(wrapper);
|
260
311
|
xfree(wrapper->client);
|
261
312
|
xfree(wrapper);
|
@@ -268,13 +319,15 @@ static VALUE allocate(VALUE klass) {
|
|
268
319
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
269
320
|
wrapper->encoding = Qnil;
|
270
321
|
wrapper->active_thread = Qnil;
|
322
|
+
wrapper->automatic_close = 1;
|
271
323
|
wrapper->server_version = 0;
|
272
324
|
wrapper->reconnect_enabled = 0;
|
273
325
|
wrapper->connect_timeout = 0;
|
274
|
-
wrapper->connected = 0; /* means that a database connection is open */
|
275
326
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
276
327
|
wrapper->refcount = 1;
|
328
|
+
wrapper->closed = 0;
|
277
329
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
330
|
+
|
278
331
|
return obj;
|
279
332
|
}
|
280
333
|
|
@@ -302,9 +355,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
302
355
|
return str;
|
303
356
|
} else {
|
304
357
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
305
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
306
358
|
rb_enc_copy(rb_str, str);
|
307
|
-
#endif
|
308
359
|
xfree(newStr);
|
309
360
|
return rb_str;
|
310
361
|
}
|
@@ -331,17 +382,45 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
331
382
|
}
|
332
383
|
|
333
384
|
rb_str = rb_str_new2(info);
|
334
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
335
385
|
rb_enc_associate(rb_str, rb_utf8_encoding());
|
336
|
-
#endif
|
337
386
|
|
338
387
|
return rb_str;
|
339
388
|
}
|
340
389
|
|
341
|
-
static VALUE
|
390
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
391
|
+
{
|
392
|
+
const char *cipher;
|
393
|
+
VALUE rb_str;
|
394
|
+
GET_CLIENT(self);
|
395
|
+
|
396
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
397
|
+
|
398
|
+
if (cipher == NULL) {
|
399
|
+
return Qnil;
|
400
|
+
}
|
401
|
+
|
402
|
+
rb_str = rb_str_new2(cipher);
|
403
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
404
|
+
|
405
|
+
return rb_str;
|
406
|
+
}
|
407
|
+
|
408
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
409
|
+
static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
|
410
|
+
{
|
411
|
+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
|
412
|
+
rb_encoding *enc = rb_to_encoding(wrapper->encoding);
|
413
|
+
key = rb_str_export_to_enc(key, enc);
|
414
|
+
value = rb_str_export_to_enc(value, enc);
|
415
|
+
|
416
|
+
mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
|
417
|
+
return ST_CONTINUE;
|
418
|
+
}
|
419
|
+
#endif
|
420
|
+
|
421
|
+
static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
|
342
422
|
struct nogvl_connect_args args;
|
343
|
-
time_t start_time, end_time;
|
344
|
-
unsigned int elapsed_time, connect_timeout;
|
423
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
345
424
|
VALUE rv;
|
346
425
|
GET_CLIENT(self);
|
347
426
|
|
@@ -354,6 +433,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
354
433
|
args.mysql = wrapper->client;
|
355
434
|
args.client_flag = NUM2ULONG(flags);
|
356
435
|
|
436
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
437
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
|
438
|
+
rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
|
439
|
+
#endif
|
440
|
+
|
357
441
|
if (wrapper->connect_timeout)
|
358
442
|
time(&start_time);
|
359
443
|
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
@@ -368,7 +452,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
368
452
|
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
369
453
|
if (elapsed_time > 0)
|
370
454
|
elapsed_time--;
|
371
|
-
if (elapsed_time >= wrapper->connect_timeout)
|
455
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
372
456
|
break;
|
373
457
|
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
374
458
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
@@ -380,30 +464,41 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
380
464
|
if (wrapper->connect_timeout)
|
381
465
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
382
466
|
if (rv == Qfalse)
|
383
|
-
|
467
|
+
rb_raise_mysql2_error(wrapper);
|
384
468
|
}
|
385
469
|
|
386
470
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
387
|
-
wrapper->connected = 1;
|
388
471
|
return self;
|
389
472
|
}
|
390
473
|
|
391
474
|
/*
|
392
|
-
* Immediately disconnect from the server
|
475
|
+
* Immediately disconnect from the server; normally the garbage collector
|
393
476
|
* will disconnect automatically when a connection is no longer needed.
|
394
477
|
* Explicitly closing this will free up server resources sooner than waiting
|
395
478
|
* for the garbage collector.
|
479
|
+
*
|
480
|
+
* @return [nil]
|
396
481
|
*/
|
397
482
|
static VALUE rb_mysql_client_close(VALUE self) {
|
398
483
|
GET_CLIENT(self);
|
399
484
|
|
400
|
-
if (wrapper->
|
485
|
+
if (wrapper->client) {
|
401
486
|
rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
|
402
487
|
}
|
403
488
|
|
404
489
|
return Qnil;
|
405
490
|
}
|
406
491
|
|
492
|
+
/* call-seq:
|
493
|
+
* client.closed?
|
494
|
+
*
|
495
|
+
* @return [Boolean]
|
496
|
+
*/
|
497
|
+
static VALUE rb_mysql_client_closed(VALUE self) {
|
498
|
+
GET_CLIENT(self);
|
499
|
+
return CONNECTED(wrapper) ? Qfalse : Qtrue;
|
500
|
+
}
|
501
|
+
|
407
502
|
/*
|
408
503
|
* mysql_send_query is unlikely to block since most queries are small
|
409
504
|
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
@@ -423,8 +518,8 @@ static VALUE do_send_query(void *args) {
|
|
423
518
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
424
519
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
425
520
|
/* an error occurred, we're not active anymore */
|
426
|
-
|
427
|
-
|
521
|
+
wrapper->active_thread = Qnil;
|
522
|
+
rb_raise_mysql2_error(wrapper);
|
428
523
|
}
|
429
524
|
return Qnil;
|
430
525
|
}
|
@@ -442,10 +537,9 @@ static void *nogvl_read_query_result(void *ptr) {
|
|
442
537
|
}
|
443
538
|
|
444
539
|
static void *nogvl_do_result(void *ptr, char use_result) {
|
445
|
-
mysql_client_wrapper *wrapper;
|
540
|
+
mysql_client_wrapper *wrapper = ptr;
|
446
541
|
MYSQL_RES *result;
|
447
542
|
|
448
|
-
wrapper = (mysql_client_wrapper *)ptr;
|
449
543
|
if (use_result) {
|
450
544
|
result = mysql_use_result(wrapper->client);
|
451
545
|
} else {
|
@@ -486,11 +580,11 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
486
580
|
REQUIRE_CONNECTED(wrapper);
|
487
581
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
488
582
|
/* an error occurred, mark this connection inactive */
|
489
|
-
|
490
|
-
|
583
|
+
wrapper->active_thread = Qnil;
|
584
|
+
rb_raise_mysql2_error(wrapper);
|
491
585
|
}
|
492
586
|
|
493
|
-
is_streaming = rb_hash_aref(
|
587
|
+
is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
|
494
588
|
if (is_streaming == Qtrue) {
|
495
589
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
496
590
|
} else {
|
@@ -499,17 +593,20 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
499
593
|
|
500
594
|
if (result == NULL) {
|
501
595
|
if (mysql_errno(wrapper->client) != 0) {
|
502
|
-
|
596
|
+
wrapper->active_thread = Qnil;
|
503
597
|
rb_raise_mysql2_error(wrapper);
|
504
598
|
}
|
505
599
|
/* no data and no error, so query was not a SELECT */
|
506
600
|
return Qnil;
|
507
601
|
}
|
508
602
|
|
509
|
-
|
510
|
-
|
603
|
+
// Duplicate the options hash and put the copy in the Result object
|
604
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
605
|
+
(void)RB_GC_GUARD(current);
|
511
606
|
Check_Type(current, T_HASH);
|
512
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
607
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
608
|
+
|
609
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
513
610
|
|
514
611
|
return resultObj;
|
515
612
|
}
|
@@ -524,31 +621,30 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
524
621
|
GET_CLIENT(self);
|
525
622
|
|
526
623
|
wrapper->active_thread = Qnil;
|
527
|
-
wrapper->connected = 0;
|
528
624
|
|
529
625
|
/* Invalidate the MySQL socket to prevent further communication.
|
530
626
|
* The GC will come along later and call mysql_close to free it.
|
531
627
|
*/
|
532
|
-
if (
|
533
|
-
|
534
|
-
|
628
|
+
if (CONNECTED(wrapper)) {
|
629
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
630
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, closing unsafely\n");
|
631
|
+
close(wrapper->client->net.fd);
|
632
|
+
}
|
633
|
+
wrapper->client->net.fd = -1;
|
535
634
|
}
|
536
635
|
|
537
636
|
rb_exc_raise(error);
|
538
|
-
|
539
|
-
return Qnil;
|
540
637
|
}
|
541
638
|
|
542
639
|
static VALUE do_query(void *args) {
|
543
|
-
struct async_query_args *async_args;
|
640
|
+
struct async_query_args *async_args = args;
|
544
641
|
struct timeval tv;
|
545
|
-
struct timeval*
|
642
|
+
struct timeval *tvp;
|
546
643
|
long int sec;
|
547
644
|
int retval;
|
548
645
|
VALUE read_timeout;
|
549
646
|
|
550
|
-
|
551
|
-
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
647
|
+
read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
|
552
648
|
|
553
649
|
tvp = NULL;
|
554
650
|
if (!NIL_P(read_timeout)) {
|
@@ -569,7 +665,7 @@ static VALUE do_query(void *args) {
|
|
569
665
|
retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
|
570
666
|
|
571
667
|
if (retval == 0) {
|
572
|
-
rb_raise(
|
668
|
+
rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
|
573
669
|
}
|
574
670
|
|
575
671
|
if (retval < 0) {
|
@@ -583,28 +679,50 @@ static VALUE do_query(void *args) {
|
|
583
679
|
|
584
680
|
return Qnil;
|
585
681
|
}
|
586
|
-
#
|
587
|
-
static VALUE finish_and_mark_inactive(void *args) {
|
588
|
-
VALUE self;
|
589
|
-
MYSQL_RES *result;
|
590
|
-
|
591
|
-
self = (VALUE)args;
|
682
|
+
#endif
|
592
683
|
|
684
|
+
static VALUE disconnect_and_mark_inactive(VALUE self) {
|
593
685
|
GET_CLIENT(self);
|
594
686
|
|
687
|
+
/* Check if execution terminated while result was still being read. */
|
595
688
|
if (!NIL_P(wrapper->active_thread)) {
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
689
|
+
if (CONNECTED(wrapper)) {
|
690
|
+
/* Invalidate the MySQL socket to prevent further communication. */
|
691
|
+
#ifndef _WIN32
|
692
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
693
|
+
rb_warn("mysql2 failed to invalidate FD safely, closing unsafely\n");
|
694
|
+
close(wrapper->client->net.fd);
|
695
|
+
}
|
696
|
+
#else
|
697
|
+
close(wrapper->client->net.fd);
|
698
|
+
#endif
|
699
|
+
wrapper->client->net.fd = -1;
|
700
|
+
}
|
701
|
+
/* Skip mysql client check performed before command execution. */
|
702
|
+
wrapper->client->status = MYSQL_STATUS_READY;
|
602
703
|
wrapper->active_thread = Qnil;
|
603
704
|
}
|
604
705
|
|
605
706
|
return Qnil;
|
606
707
|
}
|
607
|
-
|
708
|
+
|
709
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
710
|
+
VALUE thread_current = rb_thread_current();
|
711
|
+
GET_CLIENT(self);
|
712
|
+
|
713
|
+
// see if this connection is still waiting on a result from a previous query
|
714
|
+
if (NIL_P(wrapper->active_thread)) {
|
715
|
+
// mark this connection active
|
716
|
+
wrapper->active_thread = thread_current;
|
717
|
+
} else if (wrapper->active_thread == thread_current) {
|
718
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
719
|
+
} else {
|
720
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
721
|
+
const char *thr = StringValueCStr(inspect);
|
722
|
+
|
723
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
724
|
+
}
|
725
|
+
}
|
608
726
|
|
609
727
|
/* call-seq:
|
610
728
|
* client.abandon_results!
|
@@ -640,80 +758,49 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
640
758
|
* client.query(sql, options = {})
|
641
759
|
*
|
642
760
|
* Query the database with +sql+, with optional +options+. For the possible
|
643
|
-
* options, see
|
761
|
+
* options, see default_query_options on the Mysql2::Client class.
|
644
762
|
*/
|
645
|
-
static VALUE
|
763
|
+
static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
646
764
|
#ifndef _WIN32
|
647
765
|
struct async_query_args async_args;
|
648
766
|
#endif
|
649
767
|
struct nogvl_send_query_args args;
|
650
|
-
int async = 0;
|
651
|
-
VALUE opts, current;
|
652
|
-
VALUE thread_current = rb_thread_current();
|
653
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
654
|
-
rb_encoding *conn_enc;
|
655
|
-
#endif
|
656
768
|
GET_CLIENT(self);
|
657
769
|
|
658
770
|
REQUIRE_CONNECTED(wrapper);
|
659
771
|
args.mysql = wrapper->client;
|
660
772
|
|
661
|
-
|
662
|
-
RB_GC_GUARD(current);
|
773
|
+
(void)RB_GC_GUARD(current);
|
663
774
|
Check_Type(current, T_HASH);
|
664
|
-
|
775
|
+
rb_ivar_set(self, intern_current_query_options, current);
|
665
776
|
|
666
|
-
|
667
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
668
|
-
|
669
|
-
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
670
|
-
async = 1;
|
671
|
-
}
|
672
|
-
}
|
673
|
-
|
674
|
-
Check_Type(args.sql, T_STRING);
|
675
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
676
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
777
|
+
Check_Type(sql, T_STRING);
|
677
778
|
/* ensure the string is in the encoding the connection is expecting */
|
678
|
-
args.sql = rb_str_export_to_enc(
|
679
|
-
#endif
|
779
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
680
780
|
args.sql_ptr = RSTRING_PTR(args.sql);
|
681
781
|
args.sql_len = RSTRING_LEN(args.sql);
|
682
|
-
|
683
|
-
/* see if this connection is still waiting on a result from a previous query */
|
684
|
-
if (NIL_P(wrapper->active_thread)) {
|
685
|
-
/* mark this connection active */
|
686
|
-
wrapper->active_thread = thread_current;
|
687
|
-
} else if (wrapper->active_thread == thread_current) {
|
688
|
-
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
689
|
-
} else {
|
690
|
-
VALUE inspect = rb_inspect(wrapper->active_thread);
|
691
|
-
const char *thr = StringValueCStr(inspect);
|
692
|
-
|
693
|
-
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
694
|
-
RB_GC_GUARD(inspect);
|
695
|
-
}
|
696
|
-
|
697
782
|
args.wrapper = wrapper;
|
698
783
|
|
784
|
+
rb_mysql_client_set_active_thread(self);
|
785
|
+
|
699
786
|
#ifndef _WIN32
|
700
787
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
701
788
|
|
702
|
-
if (
|
789
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
790
|
+
return Qnil;
|
791
|
+
} else {
|
703
792
|
async_args.fd = wrapper->client->net.fd;
|
704
793
|
async_args.self = self;
|
705
794
|
|
706
795
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
707
796
|
|
708
|
-
return rb_mysql_client_async_result
|
709
|
-
} else {
|
710
|
-
return Qnil;
|
797
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
711
798
|
}
|
712
799
|
#else
|
713
800
|
do_send_query(&args);
|
714
801
|
|
715
802
|
/* this will just block until the result is ready */
|
716
|
-
return rb_ensure(rb_mysql_client_async_result, self,
|
803
|
+
return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
|
717
804
|
#endif
|
718
805
|
}
|
719
806
|
|
@@ -726,20 +813,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
726
813
|
unsigned char *newStr;
|
727
814
|
VALUE rb_str;
|
728
815
|
unsigned long newLen, oldLen;
|
729
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
730
816
|
rb_encoding *default_internal_enc;
|
731
817
|
rb_encoding *conn_enc;
|
732
|
-
#endif
|
733
818
|
GET_CLIENT(self);
|
734
819
|
|
735
820
|
REQUIRE_CONNECTED(wrapper);
|
736
821
|
Check_Type(str, T_STRING);
|
737
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
738
822
|
default_internal_enc = rb_default_internal_encoding();
|
739
823
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
740
824
|
/* ensure the string is in the encoding the connection is expecting */
|
741
825
|
str = rb_str_export_to_enc(str, conn_enc);
|
742
|
-
#endif
|
743
826
|
|
744
827
|
oldLen = RSTRING_LEN(str);
|
745
828
|
newStr = xmalloc(oldLen*2+1);
|
@@ -747,21 +830,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
747
830
|
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
748
831
|
if (newLen == oldLen) {
|
749
832
|
/* no need to return a new ruby string if nothing changed */
|
750
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
751
833
|
if (default_internal_enc) {
|
752
834
|
str = rb_str_export_to_enc(str, default_internal_enc);
|
753
835
|
}
|
754
|
-
#endif
|
755
836
|
xfree(newStr);
|
756
837
|
return str;
|
757
838
|
} else {
|
758
839
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
759
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
760
840
|
rb_enc_associate(rb_str, conn_enc);
|
761
841
|
if (default_internal_enc) {
|
762
842
|
rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
|
763
843
|
}
|
764
|
-
#endif
|
765
844
|
xfree(newStr);
|
766
845
|
return rb_str;
|
767
846
|
}
|
@@ -807,10 +886,12 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
807
886
|
retval = &boolval;
|
808
887
|
break;
|
809
888
|
|
889
|
+
#ifdef MYSQL_SECURE_AUTH
|
810
890
|
case MYSQL_SECURE_AUTH:
|
811
891
|
boolval = (value == Qfalse ? 0 : 1);
|
812
892
|
retval = &boolval;
|
813
893
|
break;
|
894
|
+
#endif
|
814
895
|
|
815
896
|
case MYSQL_READ_DEFAULT_FILE:
|
816
897
|
charval = (const char *)StringValueCStr(value);
|
@@ -827,6 +908,18 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
827
908
|
retval = charval;
|
828
909
|
break;
|
829
910
|
|
911
|
+
case MYSQL_DEFAULT_AUTH:
|
912
|
+
charval = (const char *)StringValueCStr(value);
|
913
|
+
retval = charval;
|
914
|
+
break;
|
915
|
+
|
916
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
917
|
+
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
918
|
+
boolval = (value == Qfalse ? 0 : 1);
|
919
|
+
retval = &boolval;
|
920
|
+
break;
|
921
|
+
#endif
|
922
|
+
|
830
923
|
default:
|
831
924
|
return Qfalse;
|
832
925
|
}
|
@@ -863,10 +956,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
863
956
|
version = rb_str_new2(mysql_get_client_info());
|
864
957
|
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
865
958
|
|
866
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
867
959
|
rb_enc_associate(version, rb_usascii_encoding());
|
868
960
|
rb_enc_associate(header_version, rb_usascii_encoding());
|
869
|
-
#endif
|
870
961
|
|
871
962
|
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
872
963
|
rb_hash_aset(version_info, sym_version, version);
|
@@ -882,27 +973,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
882
973
|
*/
|
883
974
|
static VALUE rb_mysql_client_server_info(VALUE self) {
|
884
975
|
VALUE version, server_info;
|
885
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
886
976
|
rb_encoding *default_internal_enc;
|
887
977
|
rb_encoding *conn_enc;
|
888
|
-
#endif
|
889
978
|
GET_CLIENT(self);
|
890
979
|
|
891
980
|
REQUIRE_CONNECTED(wrapper);
|
892
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
893
981
|
default_internal_enc = rb_default_internal_encoding();
|
894
982
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
895
|
-
#endif
|
896
983
|
|
897
984
|
version = rb_hash_new();
|
898
985
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
|
899
986
|
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
|
900
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
901
987
|
rb_enc_associate(server_info, conn_enc);
|
902
988
|
if (default_internal_enc) {
|
903
989
|
server_info = rb_str_export_to_enc(server_info, default_internal_enc);
|
904
990
|
}
|
905
|
-
#endif
|
906
991
|
rb_hash_aset(version, sym_version, server_info);
|
907
992
|
return version;
|
908
993
|
}
|
@@ -912,15 +997,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
912
997
|
*
|
913
998
|
* Return the file descriptor number for this client.
|
914
999
|
*/
|
915
|
-
static VALUE rb_mysql_client_socket(VALUE self) {
|
916
1000
|
#ifndef _WIN32
|
1001
|
+
static VALUE rb_mysql_client_socket(VALUE self) {
|
917
1002
|
GET_CLIENT(self);
|
918
1003
|
REQUIRE_CONNECTED(wrapper);
|
919
1004
|
return INT2NUM(wrapper->client->net.fd);
|
1005
|
+
}
|
920
1006
|
#else
|
1007
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
921
1008
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
922
|
-
#endif
|
923
1009
|
}
|
1010
|
+
#endif
|
924
1011
|
|
925
1012
|
/* call-seq:
|
926
1013
|
* client.last_id
|
@@ -1014,13 +1101,30 @@ static void *nogvl_ping(void *ptr) {
|
|
1014
1101
|
static VALUE rb_mysql_client_ping(VALUE self) {
|
1015
1102
|
GET_CLIENT(self);
|
1016
1103
|
|
1017
|
-
if (!wrapper
|
1104
|
+
if (!CONNECTED(wrapper)) {
|
1018
1105
|
return Qfalse;
|
1019
1106
|
} else {
|
1020
1107
|
return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
|
1021
1108
|
}
|
1022
1109
|
}
|
1023
1110
|
|
1111
|
+
/* call-seq:
|
1112
|
+
* client.set_server_option(value)
|
1113
|
+
*
|
1114
|
+
* Enables or disables an option for the connection.
|
1115
|
+
* Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
|
1116
|
+
* for more information.
|
1117
|
+
*/
|
1118
|
+
static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
|
1119
|
+
GET_CLIENT(self);
|
1120
|
+
|
1121
|
+
if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
|
1122
|
+
return Qtrue;
|
1123
|
+
} else {
|
1124
|
+
return Qfalse;
|
1125
|
+
}
|
1126
|
+
}
|
1127
|
+
|
1024
1128
|
/* call-seq:
|
1025
1129
|
* client.more_results?
|
1026
1130
|
*
|
@@ -1029,10 +1133,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1029
1133
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1030
1134
|
{
|
1031
1135
|
GET_CLIENT(self);
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1136
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1137
|
+
return Qfalse;
|
1138
|
+
else
|
1139
|
+
return Qtrue;
|
1036
1140
|
}
|
1037
1141
|
|
1038
1142
|
/* call-seq:
|
@@ -1079,15 +1183,15 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1079
1183
|
return Qnil;
|
1080
1184
|
}
|
1081
1185
|
|
1082
|
-
|
1083
|
-
|
1186
|
+
// Duplicate the options hash and put the copy in the Result object
|
1187
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
1188
|
+
(void)RB_GC_GUARD(current);
|
1084
1189
|
Check_Type(current, T_HASH);
|
1085
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1190
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1086
1191
|
|
1087
1192
|
return resultObj;
|
1088
1193
|
}
|
1089
1194
|
|
1090
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1091
1195
|
/* call-seq:
|
1092
1196
|
* client.encoding
|
1093
1197
|
*
|
@@ -1097,7 +1201,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1097
1201
|
GET_CLIENT(self);
|
1098
1202
|
return wrapper->encoding;
|
1099
1203
|
}
|
1204
|
+
|
1205
|
+
/* call-seq:
|
1206
|
+
* client.automatic_close?
|
1207
|
+
*
|
1208
|
+
* @return [Boolean]
|
1209
|
+
*/
|
1210
|
+
static VALUE get_automatic_close(VALUE self) {
|
1211
|
+
GET_CLIENT(self);
|
1212
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1213
|
+
}
|
1214
|
+
|
1215
|
+
/* call-seq:
|
1216
|
+
* client.automatic_close = false
|
1217
|
+
*
|
1218
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1219
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1220
|
+
* call +close+ when the connection is no longer needed.
|
1221
|
+
*
|
1222
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1223
|
+
*/
|
1224
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1225
|
+
GET_CLIENT(self);
|
1226
|
+
if (RTEST(value)) {
|
1227
|
+
wrapper->automatic_close = 1;
|
1228
|
+
} else {
|
1229
|
+
#ifndef _WIN32
|
1230
|
+
wrapper->automatic_close = 0;
|
1231
|
+
#else
|
1232
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1100
1233
|
#endif
|
1234
|
+
}
|
1235
|
+
return value;
|
1236
|
+
}
|
1101
1237
|
|
1102
1238
|
/* call-seq:
|
1103
1239
|
* client.reconnect = true
|
@@ -1134,7 +1270,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
|
|
1134
1270
|
/* Set the instance variable here even though _mysql_client_options
|
1135
1271
|
might not succeed, because the timeout is used in other ways
|
1136
1272
|
elsewhere */
|
1137
|
-
|
1273
|
+
rb_ivar_set(self, intern_read_timeout, value);
|
1138
1274
|
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
|
1139
1275
|
}
|
1140
1276
|
|
@@ -1150,19 +1286,15 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1150
1286
|
|
1151
1287
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1152
1288
|
char *charset_name;
|
1153
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1154
|
-
size_t charset_name_len;
|
1155
1289
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1156
1290
|
rb_encoding *enc;
|
1157
1291
|
VALUE rb_enc;
|
1158
|
-
#endif
|
1159
1292
|
GET_CLIENT(self);
|
1160
1293
|
|
1294
|
+
Check_Type(value, T_STRING);
|
1161
1295
|
charset_name = RSTRING_PTR(value);
|
1162
1296
|
|
1163
|
-
|
1164
|
-
charset_name_len = RSTRING_LEN(value);
|
1165
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1297
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1166
1298
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1167
1299
|
VALUE inspect = rb_inspect(value);
|
1168
1300
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1171,7 +1303,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1171
1303
|
rb_enc = rb_enc_from_encoding(enc);
|
1172
1304
|
wrapper->encoding = rb_enc;
|
1173
1305
|
}
|
1174
|
-
#endif
|
1175
1306
|
|
1176
1307
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
1177
1308
|
/* TODO: warning - unable to set charset */
|
@@ -1195,7 +1326,12 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1195
1326
|
}
|
1196
1327
|
|
1197
1328
|
static VALUE set_secure_auth(VALUE self, VALUE value) {
|
1329
|
+
/* This option was deprecated in MySQL 5.x and removed in MySQL 8.0 */
|
1330
|
+
#ifdef MYSQL_SECURE_AUTH
|
1198
1331
|
return _mysql_client_options(self, MYSQL_SECURE_AUTH, value);
|
1332
|
+
#else
|
1333
|
+
return Qfalse;
|
1334
|
+
#endif
|
1199
1335
|
}
|
1200
1336
|
|
1201
1337
|
static VALUE set_read_default_file(VALUE self, VALUE value) {
|
@@ -1210,19 +1346,43 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1210
1346
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1211
1347
|
}
|
1212
1348
|
|
1349
|
+
static VALUE set_default_auth(VALUE self, VALUE value) {
|
1350
|
+
return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
|
1351
|
+
}
|
1352
|
+
|
1353
|
+
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1354
|
+
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1355
|
+
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
1356
|
+
#else
|
1357
|
+
rb_raise(cMysql2Error, "enable-cleartext-plugin is not available, you may need a newer MySQL client library");
|
1358
|
+
#endif
|
1359
|
+
}
|
1360
|
+
|
1213
1361
|
static VALUE initialize_ext(VALUE self) {
|
1214
1362
|
GET_CLIENT(self);
|
1215
1363
|
|
1216
1364
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1217
1365
|
/* TODO: warning - not enough memory? */
|
1218
|
-
|
1366
|
+
rb_raise_mysql2_error(wrapper);
|
1219
1367
|
}
|
1220
1368
|
|
1221
1369
|
wrapper->initialized = 1;
|
1222
1370
|
return self;
|
1223
1371
|
}
|
1224
1372
|
|
1373
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1374
|
+
*
|
1375
|
+
* Create a new prepared statement.
|
1376
|
+
*/
|
1377
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1378
|
+
GET_CLIENT(self);
|
1379
|
+
REQUIRE_CONNECTED(wrapper);
|
1380
|
+
|
1381
|
+
return rb_mysql_stmt_new(self, sql);
|
1382
|
+
}
|
1383
|
+
|
1225
1384
|
void init_mysql2_client() {
|
1385
|
+
#ifdef _WIN32
|
1226
1386
|
/* verify the libmysql we're about to use was the version we were built against
|
1227
1387
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1228
1388
|
int i;
|
@@ -1237,15 +1397,14 @@ void init_mysql2_client() {
|
|
1237
1397
|
}
|
1238
1398
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1239
1399
|
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
|
1240
|
-
return;
|
1241
1400
|
}
|
1242
1401
|
}
|
1402
|
+
#endif
|
1243
1403
|
|
1244
1404
|
/* Initializing mysql library, so different threads could call Client.new */
|
1245
1405
|
/* without race condition in the library */
|
1246
1406
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1247
1407
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1248
|
-
return;
|
1249
1408
|
}
|
1250
1409
|
|
1251
1410
|
#if 0
|
@@ -1259,7 +1418,7 @@ void init_mysql2_client() {
|
|
1259
1418
|
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1260
1419
|
|
1261
1420
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1262
|
-
rb_define_method(cMysql2Client, "
|
1421
|
+
rb_define_method(cMysql2Client, "closed?", rb_mysql_client_closed, 0);
|
1263
1422
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1264
1423
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1265
1424
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
@@ -1267,18 +1426,21 @@ void init_mysql2_client() {
|
|
1267
1426
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1268
1427
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1269
1428
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1429
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1270
1430
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1271
1431
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1272
1432
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1433
|
+
rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
|
1273
1434
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1274
1435
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1275
1436
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1437
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1438
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1276
1439
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1277
1440
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1278
1441
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1279
|
-
|
1442
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1280
1443
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1281
|
-
#endif
|
1282
1444
|
|
1283
1445
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1284
1446
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1289,9 +1451,13 @@ void init_mysql2_client() {
|
|
1289
1451
|
rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
|
1290
1452
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1291
1453
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1454
|
+
rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
|
1292
1455
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1456
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1457
|
+
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1293
1458
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1294
|
-
rb_define_private_method(cMysql2Client, "connect",
|
1459
|
+
rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
|
1460
|
+
rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
|
1295
1461
|
|
1296
1462
|
sym_id = ID2SYM(rb_intern("id"));
|
1297
1463
|
sym_version = ID2SYM(rb_intern("version"));
|
@@ -1302,14 +1468,24 @@ void init_mysql2_client() {
|
|
1302
1468
|
sym_array = ID2SYM(rb_intern("array"));
|
1303
1469
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1304
1470
|
|
1471
|
+
sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
|
1472
|
+
sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
|
1473
|
+
sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
|
1474
|
+
|
1475
|
+
intern_brackets = rb_intern("[]");
|
1305
1476
|
intern_merge = rb_intern("merge");
|
1306
1477
|
intern_merge_bang = rb_intern("merge!");
|
1307
|
-
|
1308
|
-
|
1478
|
+
intern_new_with_args = rb_intern("new_with_args");
|
1479
|
+
intern_current_query_options = rb_intern("@current_query_options");
|
1480
|
+
intern_read_timeout = rb_intern("@read_timeout");
|
1309
1481
|
|
1310
1482
|
#ifdef CLIENT_LONG_PASSWORD
|
1311
1483
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
1312
1484
|
LONG2NUM(CLIENT_LONG_PASSWORD));
|
1485
|
+
#else
|
1486
|
+
/* HACK because MariaDB 10.2 no longer defines this constant,
|
1487
|
+
* but we're using it in our default connection flags. */
|
1488
|
+
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"), INT2NUM(0));
|
1313
1489
|
#endif
|
1314
1490
|
|
1315
1491
|
#ifdef CLIENT_FOUND_ROWS
|
@@ -1387,6 +1563,16 @@ void init_mysql2_client() {
|
|
1387
1563
|
rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
|
1388
1564
|
#endif
|
1389
1565
|
|
1566
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
|
1567
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
|
1568
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
|
1569
|
+
#endif
|
1570
|
+
|
1571
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
1572
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
|
1573
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
|
1574
|
+
#endif
|
1575
|
+
|
1390
1576
|
#ifdef CLIENT_MULTI_STATEMENTS
|
1391
1577
|
rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
|
1392
1578
|
LONG2NUM(CLIENT_MULTI_STATEMENTS));
|
@@ -1416,4 +1602,67 @@ void init_mysql2_client() {
|
|
1416
1602
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1417
1603
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1418
1604
|
#endif
|
1605
|
+
|
1606
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
1607
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1608
|
+
LONG2NUM(CLIENT_CONNECT_ATTRS));
|
1609
|
+
#else
|
1610
|
+
/* HACK because MySQL 5.5 and earlier don't define this constant,
|
1611
|
+
* but we're using it in our default connection flags. */
|
1612
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1613
|
+
INT2NUM(0));
|
1614
|
+
#endif
|
1615
|
+
|
1616
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1617
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1618
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1619
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1620
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1621
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1622
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1623
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1624
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1625
|
+
#endif
|
1626
|
+
|
1627
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1628
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1629
|
+
#endif
|
1630
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1631
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1632
|
+
#endif
|
1633
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1634
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1635
|
+
#endif
|
1636
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1637
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1638
|
+
#endif
|
1639
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1640
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1641
|
+
#endif
|
1642
|
+
}
|
1643
|
+
|
1644
|
+
#define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
|
1645
|
+
|
1646
|
+
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
|
1647
|
+
VALUE server_flags = rb_hash_new();
|
1648
|
+
|
1649
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
|
1650
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
|
1651
|
+
#else
|
1652
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
|
1653
|
+
#endif
|
1654
|
+
|
1655
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
|
1656
|
+
rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
|
1657
|
+
#else
|
1658
|
+
rb_hash_aset(server_flags, sym_no_index_used, Qnil);
|
1659
|
+
#endif
|
1660
|
+
|
1661
|
+
#ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
|
1662
|
+
rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
|
1663
|
+
#else
|
1664
|
+
rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
|
1665
|
+
#endif
|
1666
|
+
|
1667
|
+
rb_iv_set(result, "@server_flags", server_flags);
|
1419
1668
|
}
|