mysql2 0.4.2-x64-mingw32 → 0.4.3-x64-mingw32
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 +6 -5
- data/ext/mysql2/client.c +78 -22
- data/ext/mysql2/client.h +1 -4
- data/ext/mysql2/result.c +12 -5
- data/ext/mysql2/statement.c +30 -20
- data/lib/mysql2/2.0/mysql2.so +0 -0
- data/lib/mysql2/2.1/mysql2.so +0 -0
- data/lib/mysql2/2.2/mysql2.so +0 -0
- data/lib/mysql2/2.3/mysql2.so +0 -0
- data/lib/mysql2/client.rb +3 -3
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +88 -36
- data/spec/mysql2/result_spec.rb +4 -0
- data/spec/mysql2/statement_spec.rb +11 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02b06d1845bb8383384eb08fd3aed4d51e65a52f
|
4
|
+
data.tar.gz: 78a3ea0d05dd09bdfe0daa41bc2d18951c932afd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e9ab24baf5ac85380fe6d14198b88ae2cc1294a385c85a0a99ae48fd3197f3a6ff790b41db6269b6a03593394ff61cd693fc62f225c087fb52ddb164756a709
|
7
|
+
data.tar.gz: 5be83a0f74b810a12549557919594d7e992fd5ea9279ae252470dd26b8729926b0ebfe6cf0c35cad2d3b70bb7197c9a7126f1c406f74751aafc00705a1dd1c28
|
data/README.md
CHANGED
@@ -490,14 +490,15 @@ This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
|
490
490
|
|
491
491
|
This gem is tested with the following MySQL and MariaDB versions:
|
492
492
|
|
493
|
-
* MySQL 5.5, 5.7
|
493
|
+
* MySQL 5.5, 5.6, 5.7
|
494
494
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
495
|
-
* MariaDB 5.5, 10.0
|
495
|
+
* MariaDB 5.5, 10.0, 10.1
|
496
496
|
|
497
|
-
### Active Record
|
497
|
+
### Rails / Active Record
|
498
498
|
|
499
|
-
* mysql2 0.
|
500
|
-
* mysql2 0.3.x
|
499
|
+
* mysql2 0.4.x works with Active Record 4.2.5 and higher.
|
500
|
+
* mysql2 0.3.x works with Active Record 3.1 and higher (the AR adapter is now included in AR proper).
|
501
|
+
* mysql2 0.2.x includes an Active Record adapter compatible with AR 2.3 and 3.0, and should not be used with AR 3.1 or higher.
|
501
502
|
|
502
503
|
### Asynchronous Active Record
|
503
504
|
|
data/ext/mysql2/client.c
CHANGED
@@ -234,7 +234,7 @@ void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
234
234
|
|
235
235
|
if (wrapper->refcount == 0) {
|
236
236
|
#ifndef _WIN32
|
237
|
-
if (wrapper->connected) {
|
237
|
+
if (wrapper->connected && !wrapper->automatic_close) {
|
238
238
|
/* The client is being garbage collected while connected. Prevent
|
239
239
|
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
240
240
|
* the socket by invalidating it. invalidate_fd() will drop this
|
@@ -259,7 +259,8 @@ static VALUE allocate(VALUE klass) {
|
|
259
259
|
mysql_client_wrapper * wrapper;
|
260
260
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
261
261
|
wrapper->encoding = Qnil;
|
262
|
-
|
262
|
+
wrapper->active_thread = Qnil;
|
263
|
+
wrapper->automatic_close = 1;
|
263
264
|
wrapper->server_version = 0;
|
264
265
|
wrapper->reconnect_enabled = 0;
|
265
266
|
wrapper->connect_timeout = 0;
|
@@ -331,6 +332,26 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
331
332
|
return rb_str;
|
332
333
|
}
|
333
334
|
|
335
|
+
static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
336
|
+
{
|
337
|
+
const char *cipher;
|
338
|
+
VALUE rb_str;
|
339
|
+
GET_CLIENT(self);
|
340
|
+
|
341
|
+
cipher = mysql_get_ssl_cipher(wrapper->client);
|
342
|
+
|
343
|
+
if (cipher == NULL) {
|
344
|
+
return Qnil;
|
345
|
+
}
|
346
|
+
|
347
|
+
rb_str = rb_str_new2(cipher);
|
348
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
349
|
+
rb_enc_associate(rb_str, rb_utf8_encoding());
|
350
|
+
#endif
|
351
|
+
|
352
|
+
return rb_str;
|
353
|
+
}
|
354
|
+
|
334
355
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
335
356
|
struct nogvl_connect_args args;
|
336
357
|
time_t start_time, end_time, elapsed_time, connect_timeout;
|
@@ -372,7 +393,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
372
393
|
if (wrapper->connect_timeout)
|
373
394
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &wrapper->connect_timeout);
|
374
395
|
if (rv == Qfalse)
|
375
|
-
|
396
|
+
rb_raise_mysql2_error(wrapper);
|
376
397
|
}
|
377
398
|
|
378
399
|
wrapper->server_version = mysql_get_server_version(wrapper->client);
|
@@ -381,13 +402,12 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
381
402
|
}
|
382
403
|
|
383
404
|
/*
|
384
|
-
*
|
385
|
-
*
|
386
|
-
*
|
387
|
-
*
|
405
|
+
* Immediately disconnect from the server; normally the garbage collector
|
406
|
+
* will disconnect automatically when a connection is no longer needed.
|
407
|
+
* Explicitly closing this will free up server resources sooner than waiting
|
408
|
+
* for the garbage collector.
|
388
409
|
*
|
389
|
-
* @
|
390
|
-
* @return [void]
|
410
|
+
* @return [nil]
|
391
411
|
*/
|
392
412
|
static VALUE rb_mysql_client_close(VALUE self) {
|
393
413
|
GET_CLIENT(self);
|
@@ -418,8 +438,8 @@ static VALUE do_send_query(void *args) {
|
|
418
438
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
419
439
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
420
440
|
/* an error occurred, we're not active anymore */
|
421
|
-
|
422
|
-
|
441
|
+
wrapper->active_thread = Qnil;
|
442
|
+
rb_raise_mysql2_error(wrapper);
|
423
443
|
}
|
424
444
|
return Qnil;
|
425
445
|
}
|
@@ -448,7 +468,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
448
468
|
|
449
469
|
/* once our result is stored off, this connection is
|
450
470
|
ready for another command to be issued */
|
451
|
-
|
471
|
+
wrapper->active_thread = Qnil;
|
452
472
|
|
453
473
|
return result;
|
454
474
|
}
|
@@ -480,8 +500,8 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
480
500
|
REQUIRE_CONNECTED(wrapper);
|
481
501
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
482
502
|
/* an error occurred, mark this connection inactive */
|
483
|
-
|
484
|
-
|
503
|
+
wrapper->active_thread = Qnil;
|
504
|
+
rb_raise_mysql2_error(wrapper);
|
485
505
|
}
|
486
506
|
|
487
507
|
is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
@@ -493,7 +513,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
493
513
|
|
494
514
|
if (result == NULL) {
|
495
515
|
if (mysql_errno(wrapper->client) != 0) {
|
496
|
-
|
516
|
+
wrapper->active_thread = Qnil;
|
497
517
|
rb_raise_mysql2_error(wrapper);
|
498
518
|
}
|
499
519
|
/* no data and no error, so query was not a SELECT */
|
@@ -517,7 +537,7 @@ struct async_query_args {
|
|
517
537
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
518
538
|
GET_CLIENT(self);
|
519
539
|
|
520
|
-
|
540
|
+
wrapper->active_thread = Qnil;
|
521
541
|
wrapper->connected = 0;
|
522
542
|
|
523
543
|
/* Invalidate the MySQL socket to prevent further communication.
|
@@ -588,7 +608,7 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
588
608
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
589
609
|
mysql_free_result(result);
|
590
610
|
|
591
|
-
|
611
|
+
wrapper->active_thread = Qnil;
|
592
612
|
}
|
593
613
|
|
594
614
|
return Qnil;
|
@@ -1011,10 +1031,10 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1011
1031
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
1012
1032
|
{
|
1013
1033
|
GET_CLIENT(self);
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1034
|
+
if (mysql_more_results(wrapper->client) == 0)
|
1035
|
+
return Qfalse;
|
1036
|
+
else
|
1037
|
+
return Qtrue;
|
1018
1038
|
}
|
1019
1039
|
|
1020
1040
|
/* call-seq:
|
@@ -1081,6 +1101,39 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1081
1101
|
}
|
1082
1102
|
#endif
|
1083
1103
|
|
1104
|
+
/* call-seq:
|
1105
|
+
* client.automatic_close?
|
1106
|
+
*
|
1107
|
+
* @return [Boolean]
|
1108
|
+
*/
|
1109
|
+
static VALUE get_automatic_close(VALUE self) {
|
1110
|
+
GET_CLIENT(self);
|
1111
|
+
return wrapper->automatic_close ? Qtrue : Qfalse;
|
1112
|
+
}
|
1113
|
+
|
1114
|
+
/* call-seq:
|
1115
|
+
* client.automatic_close = false
|
1116
|
+
*
|
1117
|
+
* Set this to +false+ to leave the connection open after it is garbage
|
1118
|
+
* collected. To avoid "Aborted connection" errors on the server, explicitly
|
1119
|
+
* call +close+ when the connection is no longer needed.
|
1120
|
+
*
|
1121
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
1122
|
+
*/
|
1123
|
+
static VALUE set_automatic_close(VALUE self, VALUE value) {
|
1124
|
+
GET_CLIENT(self);
|
1125
|
+
if (RTEST(value)) {
|
1126
|
+
wrapper->automatic_close = 1;
|
1127
|
+
} else {
|
1128
|
+
#ifndef _WIN32
|
1129
|
+
wrapper->automatic_close = 0;
|
1130
|
+
#else
|
1131
|
+
rb_warn("Connections are always closed by garbage collector on Windows");
|
1132
|
+
#endif
|
1133
|
+
}
|
1134
|
+
return value;
|
1135
|
+
}
|
1136
|
+
|
1084
1137
|
/* call-seq:
|
1085
1138
|
* client.reconnect = true
|
1086
1139
|
*
|
@@ -1195,7 +1248,7 @@ static VALUE initialize_ext(VALUE self) {
|
|
1195
1248
|
|
1196
1249
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_init, wrapper, RUBY_UBF_IO, 0) == Qfalse) {
|
1197
1250
|
/* TODO: warning - not enough memory? */
|
1198
|
-
|
1251
|
+
rb_raise_mysql2_error(wrapper);
|
1199
1252
|
}
|
1200
1253
|
|
1201
1254
|
wrapper->initialized = 1;
|
@@ -1264,9 +1317,12 @@ void init_mysql2_client() {
|
|
1264
1317
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1265
1318
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1266
1319
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1320
|
+
rb_define_method(cMysql2Client, "automatic_close?", get_automatic_close, 0);
|
1321
|
+
rb_define_method(cMysql2Client, "automatic_close=", set_automatic_close, 1);
|
1267
1322
|
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1268
1323
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1269
1324
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1325
|
+
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1270
1326
|
#ifdef HAVE_RUBY_ENCODING_H
|
1271
1327
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1272
1328
|
#endif
|
data/ext/mysql2/client.h
CHANGED
@@ -43,6 +43,7 @@ typedef struct {
|
|
43
43
|
int reconnect_enabled;
|
44
44
|
unsigned int connect_timeout;
|
45
45
|
int active;
|
46
|
+
int automatic_close;
|
46
47
|
int connected;
|
47
48
|
int initialized;
|
48
49
|
int refcount;
|
@@ -58,10 +59,6 @@ typedef struct {
|
|
58
59
|
|
59
60
|
void rb_mysql_client_set_active_thread(VALUE self);
|
60
61
|
|
61
|
-
#define MARK_CONN_INACTIVE(conn) do {\
|
62
|
-
wrapper->active_thread = Qnil; \
|
63
|
-
} while(0)
|
64
|
-
|
65
62
|
#define GET_CLIENT(self) \
|
66
63
|
mysql_client_wrapper *wrapper; \
|
67
64
|
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
|
data/ext/mysql2/result.c
CHANGED
@@ -104,6 +104,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
104
104
|
wrapper->stmt_wrapper->stmt->bind_result_done = 0;
|
105
105
|
}
|
106
106
|
|
107
|
+
if (wrapper->statement != Qnil) {
|
108
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
109
|
+
}
|
110
|
+
|
107
111
|
if (wrapper->result_buffers) {
|
108
112
|
unsigned int i;
|
109
113
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
@@ -136,13 +140,15 @@ static void rb_mysql_result_free(void *ptr) {
|
|
136
140
|
decr_mysql2_client(wrapper->client_wrapper);
|
137
141
|
}
|
138
142
|
|
139
|
-
if (wrapper->statement != Qnil) {
|
140
|
-
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
141
|
-
}
|
142
|
-
|
143
143
|
xfree(wrapper);
|
144
144
|
}
|
145
145
|
|
146
|
+
static VALUE rb_mysql_result_free_(VALUE self) {
|
147
|
+
GET_RESULT(self);
|
148
|
+
rb_mysql_result_free_result(wrapper);
|
149
|
+
return Qnil;
|
150
|
+
}
|
151
|
+
|
146
152
|
/*
|
147
153
|
* for small results, this won't hit the network, but there's no
|
148
154
|
* reliable way for us to tell this so we'll always release the GVL
|
@@ -511,7 +517,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
511
517
|
return rowVal;
|
512
518
|
}
|
513
519
|
|
514
|
-
|
515
520
|
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
516
521
|
{
|
517
522
|
VALUE rowVal;
|
@@ -911,6 +916,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
911
916
|
if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
|
912
917
|
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
913
918
|
if (wrapper->numberOfRows == 0) {
|
919
|
+
rb_mysql_result_free_result(wrapper);
|
914
920
|
wrapper->rows = rb_ary_new();
|
915
921
|
return wrapper->rows;
|
916
922
|
}
|
@@ -1007,6 +1013,7 @@ void init_mysql2_result() {
|
|
1007
1013
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
1008
1014
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
1009
1015
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
1016
|
+
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
1010
1017
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
1011
1018
|
rb_define_alias(cMysql2Result, "size", "count");
|
1012
1019
|
|
data/ext/mysql2/statement.c
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
VALUE cMysql2Statement;
|
4
4
|
extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
|
5
5
|
static VALUE sym_stream, intern_new_with_args, intern_each;
|
6
|
-
static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
|
6
|
+
static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year, intern_to_s;
|
7
7
|
|
8
8
|
#define GET_STATEMENT(self) \
|
9
9
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -11,7 +11,6 @@ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, inter
|
|
11
11
|
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
|
12
12
|
if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
|
13
13
|
|
14
|
-
|
15
14
|
static void rb_mysql_stmt_mark(void * ptr) {
|
16
15
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
17
16
|
if (!stmt_wrapper) return;
|
@@ -42,7 +41,6 @@ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
|
|
42
41
|
}
|
43
42
|
}
|
44
43
|
|
45
|
-
|
46
44
|
void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
47
45
|
VALUE e;
|
48
46
|
GET_CLIENT(stmt_wrapper->client);
|
@@ -71,7 +69,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
71
69
|
rb_exc_raise(e);
|
72
70
|
}
|
73
71
|
|
74
|
-
|
75
72
|
/*
|
76
73
|
* used to pass all arguments to mysql_stmt_prepare while inside
|
77
74
|
* nogvl_prepare_statement_args
|
@@ -180,14 +177,17 @@ static void *nogvl_execute(void *ptr) {
|
|
180
177
|
}
|
181
178
|
}
|
182
179
|
|
183
|
-
static void *
|
184
|
-
|
180
|
+
static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
|
181
|
+
unsigned long length;
|
185
182
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
183
|
+
bind_buffer->buffer_type = MYSQL_TYPE_STRING;
|
184
|
+
bind_buffer->buffer = RSTRING_PTR(string);
|
185
|
+
|
186
|
+
length = RSTRING_LEN(string);
|
187
|
+
bind_buffer->buffer_length = length;
|
188
|
+
*length_buffer = length;
|
189
|
+
|
190
|
+
bind_buffer->length = length_buffer;
|
191
191
|
}
|
192
192
|
|
193
193
|
/* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means
|
@@ -280,11 +280,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
280
280
|
#ifdef HAVE_RUBY_ENCODING_H
|
281
281
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
282
282
|
#endif
|
283
|
-
bind_buffers[i]
|
284
|
-
bind_buffers[i].buffer = RSTRING_PTR(params_enc[i]);
|
285
|
-
bind_buffers[i].buffer_length = RSTRING_LEN(params_enc[i]);
|
286
|
-
length_buffers[i] = bind_buffers[i].buffer_length;
|
287
|
-
bind_buffers[i].length = &length_buffers[i];
|
283
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
288
284
|
}
|
289
285
|
break;
|
290
286
|
default:
|
@@ -324,6 +320,19 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
324
320
|
*(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
|
325
321
|
} else if (CLASS_OF(argv[i]) == cBigDecimal) {
|
326
322
|
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
323
|
+
|
324
|
+
// DECIMAL are represented with the "string representation of the
|
325
|
+
// original server-side value", see
|
326
|
+
// https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html
|
327
|
+
// This should be independent of the locale used both on the server
|
328
|
+
// and the client side.
|
329
|
+
VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
|
330
|
+
|
331
|
+
params_enc[i] = rb_val_as_string;
|
332
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
333
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
334
|
+
#endif
|
335
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
327
336
|
}
|
328
337
|
break;
|
329
338
|
}
|
@@ -347,8 +356,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
347
356
|
if (metadata == NULL) {
|
348
357
|
if (mysql_stmt_errno(stmt) != 0) {
|
349
358
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
350
|
-
|
351
|
-
MARK_CONN_INACTIVE(stmt_wrapper->client);
|
359
|
+
wrapper->active_thread = Qnil;
|
352
360
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
353
361
|
}
|
354
362
|
// no data and no error, so query was not a SELECT
|
@@ -362,11 +370,11 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
362
370
|
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
363
371
|
if (!is_streaming) {
|
364
372
|
// recieve the whole result set from the server
|
365
|
-
if (
|
373
|
+
if (mysql_stmt_store_result(stmt)) {
|
366
374
|
mysql_free_result(metadata);
|
367
375
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
368
376
|
}
|
369
|
-
|
377
|
+
wrapper->active_thread = Qnil;
|
370
378
|
}
|
371
379
|
|
372
380
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
@@ -491,4 +499,6 @@ void init_mysql2_statement() {
|
|
491
499
|
intern_day = rb_intern("day");
|
492
500
|
intern_month = rb_intern("month");
|
493
501
|
intern_year = rb_intern("year");
|
502
|
+
|
503
|
+
intern_to_s = rb_intern("to_s");
|
494
504
|
}
|
data/lib/mysql2/2.0/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/2.1/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/2.2/mysql2.so
CHANGED
Binary file
|
Binary file
|
data/lib/mysql2/client.rb
CHANGED
@@ -30,13 +30,13 @@ module Mysql2
|
|
30
30
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
31
31
|
|
32
32
|
# TODO: stricter validation rather than silent massaging
|
33
|
-
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command].each do |key|
|
33
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
|
34
34
|
next unless opts.key?(key)
|
35
35
|
case key
|
36
|
-
when :reconnect, :local_infile, :secure_auth
|
36
|
+
when :reconnect, :local_infile, :secure_auth, :automatic_close
|
37
37
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
38
38
|
when :connect_timeout, :read_timeout, :write_timeout
|
39
|
-
send(:"#{key}=", opts[key].
|
39
|
+
send(:"#{key}=", opts[key]) unless opts[key].nil?
|
40
40
|
else
|
41
41
|
send(:"#{key}=", opts[key])
|
42
42
|
end
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'spec_helper'
|
3
|
+
require 'stringio'
|
3
4
|
|
4
5
|
RSpec.describe Mysql2::Client do
|
5
6
|
context "using defaults file" do
|
@@ -145,16 +146,12 @@ RSpec.describe Mysql2::Client do
|
|
145
146
|
# rubocop:enable Style/TrailingComma
|
146
147
|
}.not_to raise_error
|
147
148
|
|
148
|
-
results = ssl_client.query(
|
149
|
-
expect(results[
|
150
|
-
expect(results[
|
151
|
-
expect(results[0]['Value']).to be_an_instance_of(String)
|
152
|
-
expect(results[0]['Value']).not_to be_empty
|
149
|
+
results = Hash[ssl_client.query('SHOW STATUS WHERE Variable_name LIKE "Ssl_%"').map { |x| x.values_at('Variable_name', 'Value') }]
|
150
|
+
expect(results['Ssl_cipher']).not_to be_empty
|
151
|
+
expect(results['Ssl_version']).not_to be_empty
|
153
152
|
|
154
|
-
expect(
|
155
|
-
expect(results[
|
156
|
-
expect(results[1]['Value']).to be_an_instance_of(String)
|
157
|
-
expect(results[1]['Value']).not_to be_empty
|
153
|
+
expect(ssl_client.ssl_cipher).not_to be_empty
|
154
|
+
expect(results['Ssl_cipher']).to eql(ssl_client.ssl_cipher)
|
158
155
|
|
159
156
|
ssl_client.close
|
160
157
|
end
|
@@ -172,48 +169,97 @@ RSpec.describe Mysql2::Client do
|
|
172
169
|
expect {
|
173
170
|
Mysql2::Client.new(DatabaseCredentials['root']).close
|
174
171
|
}.to_not change {
|
175
|
-
@client.query("SHOW STATUS LIKE '
|
172
|
+
@client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
|
173
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
|
176
174
|
}
|
177
175
|
end
|
178
176
|
|
179
177
|
it "should not leave dangling connections after garbage collection" do
|
180
178
|
run_gc
|
179
|
+
expect {
|
180
|
+
expect {
|
181
|
+
10.times do
|
182
|
+
Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
|
183
|
+
end
|
184
|
+
}.to change {
|
185
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
186
|
+
}.by(10)
|
181
187
|
|
182
|
-
|
183
|
-
|
188
|
+
run_gc
|
189
|
+
}.to_not change {
|
190
|
+
@client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
|
191
|
+
@client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
|
192
|
+
}
|
193
|
+
end
|
184
194
|
|
185
|
-
|
186
|
-
|
195
|
+
context "#automatic_close" do
|
196
|
+
it "is enabled by default" do
|
197
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
198
|
+
expect(client.automatic_close?).to be(true)
|
187
199
|
end
|
188
|
-
after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
|
189
|
-
expect(after_count).to eq(before_count + 10)
|
190
200
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
201
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
202
|
+
it "cannot be disabled" do
|
203
|
+
stderr, $stderr = $stderr, StringIO.new
|
195
204
|
|
196
|
-
|
197
|
-
|
205
|
+
begin
|
206
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
|
207
|
+
expect($stderr.string).to include('always closed by garbage collector')
|
208
|
+
$stderr.reopen
|
198
209
|
|
199
|
-
|
200
|
-
|
210
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
211
|
+
client.automatic_close = false
|
212
|
+
expect($stderr.string).to include('always closed by garbage collector')
|
213
|
+
$stderr.reopen
|
214
|
+
|
215
|
+
expect { client.automatic_close = true }.to_not change { $stderr.string }
|
216
|
+
ensure
|
217
|
+
$stderr = stderr
|
218
|
+
end
|
219
|
+
end
|
220
|
+
else
|
221
|
+
it "can be configured" do
|
222
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
|
223
|
+
expect(client.automatic_close?).to be(false)
|
224
|
+
end
|
201
225
|
|
202
|
-
|
203
|
-
|
204
|
-
|
226
|
+
it "can be assigned" do
|
227
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
228
|
+
client.automatic_close = false
|
229
|
+
expect(client.automatic_close?).to be(false)
|
205
230
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
231
|
+
client.automatic_close = true
|
232
|
+
expect(client.automatic_close?).to be(true)
|
233
|
+
|
234
|
+
client.automatic_close = nil
|
235
|
+
expect(client.automatic_close?).to be(false)
|
236
|
+
|
237
|
+
client.automatic_close = 9
|
238
|
+
expect(client.automatic_close?).to be(true)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should not close connections when running in a child process" do
|
242
|
+
run_gc
|
243
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
244
|
+
client.automatic_close = false
|
211
245
|
|
212
|
-
|
246
|
+
# this empty `fork` call fixes this tests on RBX; without it, the next
|
247
|
+
# `fork` call hangs forever. WTF?
|
248
|
+
fork {}
|
213
249
|
|
214
|
-
|
215
|
-
|
216
|
-
|
250
|
+
fork do
|
251
|
+
client.query('SELECT 1')
|
252
|
+
client = nil
|
253
|
+
run_gc
|
254
|
+
end
|
255
|
+
|
256
|
+
Process.wait
|
257
|
+
|
258
|
+
# this will throw an error if the underlying socket was shutdown by the
|
259
|
+
# child's GC
|
260
|
+
expect { client.query('SELECT 1') }.to_not raise_exception
|
261
|
+
end
|
262
|
+
end
|
217
263
|
end
|
218
264
|
|
219
265
|
it "should be able to connect to database with numeric-only name" do
|
@@ -352,6 +398,12 @@ RSpec.describe Mysql2::Client do
|
|
352
398
|
}.to raise_error(Mysql2::Error)
|
353
399
|
end
|
354
400
|
|
401
|
+
it "should allow nil read_timeout" do
|
402
|
+
client = Mysql2::Client.new(:read_timeout => nil)
|
403
|
+
|
404
|
+
expect(client.read_timeout).to be_nil
|
405
|
+
end
|
406
|
+
|
355
407
|
context "#query" do
|
356
408
|
it "should let you query again if iterating is finished when streaming" do
|
357
409
|
@client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -22,6 +22,10 @@ RSpec.describe Mysql2::Result do
|
|
22
22
|
expect(@result).to respond_to(:each)
|
23
23
|
end
|
24
24
|
|
25
|
+
it "should respond to #free" do
|
26
|
+
expect(@result).to respond_to(:free)
|
27
|
+
end
|
28
|
+
|
25
29
|
it "should raise a Mysql2::Error exception upon a bad query" do
|
26
30
|
expect {
|
27
31
|
@client.query "bad sql"
|
@@ -117,6 +117,17 @@ RSpec.describe Mysql2::Statement do
|
|
117
117
|
expect(list[1]).to eq('2')
|
118
118
|
end
|
119
119
|
|
120
|
+
it "should update a DECIMAL value passing a BigDecimal" do
|
121
|
+
@client.query 'USE test'
|
122
|
+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
|
123
|
+
@client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))'
|
124
|
+
|
125
|
+
@client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal.new("123.45"))
|
126
|
+
|
127
|
+
test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first
|
128
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
129
|
+
end
|
130
|
+
|
120
131
|
context "utf8_db" do
|
121
132
|
before(:each) do
|
122
133
|
@client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.3
|
5
5
|
platform: x64-mingw32
|
6
6
|
authors:
|
7
7
|
- Brian Lopez
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-02-24 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email:
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/mysql2/2.0/mysql2.so
|
43
43
|
- lib/mysql2/2.1/mysql2.so
|
44
44
|
- lib/mysql2/2.2/mysql2.so
|
45
|
+
- lib/mysql2/2.3/mysql2.so
|
45
46
|
- lib/mysql2/client.rb
|
46
47
|
- lib/mysql2/console.rb
|
47
48
|
- lib/mysql2/em.rb
|
@@ -114,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
115
|
version: '0'
|
115
116
|
requirements: []
|
116
117
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.
|
118
|
+
rubygems_version: 2.5.1
|
118
119
|
signing_key:
|
119
120
|
specification_version: 4
|
120
121
|
summary: A simple, fast Mysql library for Ruby, binding to libmysql
|