mysql2 0.4.2 → 0.4.10
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 +77 -51
- data/ext/mysql2/client.c +242 -51
- data/ext/mysql2/client.h +2 -12
- data/ext/mysql2/extconf.rb +26 -2
- data/ext/mysql2/mysql2_ext.h +0 -4
- data/ext/mysql2/result.c +40 -22
- data/ext/mysql2/result.h +3 -2
- data/ext/mysql2/statement.c +133 -32
- data/lib/mysql2/client.rb +19 -6
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +1 -0
- data/spec/mysql2/client_spec.rb +211 -111
- data/spec/mysql2/error_spec.rb +4 -6
- data/spec/mysql2/result_spec.rb +71 -36
- data/spec/mysql2/statement_spec.rb +144 -52
- data/spec/spec_helper.rb +73 -59
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- metadata +12 -11
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
|
@@ -256,8 +262,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
|
|
256
262
|
if (wrapper->result_buffers != NULL) return;
|
257
263
|
|
258
264
|
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
259
|
-
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(
|
260
|
-
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(
|
265
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
266
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
261
267
|
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
262
268
|
|
263
269
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
@@ -340,15 +346,15 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
340
346
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
341
347
|
#endif
|
342
348
|
|
349
|
+
if (wrapper->fields == Qnil) {
|
350
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
351
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
352
|
+
}
|
343
353
|
if (args->asArray) {
|
344
354
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
345
355
|
} else {
|
346
356
|
rowVal = rb_hash_new();
|
347
357
|
}
|
348
|
-
if (wrapper->fields == Qnil) {
|
349
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
350
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
351
|
-
}
|
352
358
|
|
353
359
|
if (wrapper->result_buffers == NULL) {
|
354
360
|
rb_mysql_result_alloc_result_buffers(self, fields);
|
@@ -399,6 +405,13 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
399
405
|
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
400
406
|
}
|
401
407
|
break;
|
408
|
+
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
409
|
+
if (args->castBool && fields[i].length == 1) {
|
410
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
411
|
+
}else{
|
412
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
413
|
+
}
|
414
|
+
break;
|
402
415
|
case MYSQL_TYPE_SHORT: // short int
|
403
416
|
if (result_buffer->is_unsigned) {
|
404
417
|
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
@@ -488,7 +501,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
488
501
|
case MYSQL_TYPE_BLOB: // char[]
|
489
502
|
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
490
503
|
case MYSQL_TYPE_LONG_BLOB: // char[]
|
491
|
-
case MYSQL_TYPE_BIT: // char[]
|
492
504
|
case MYSQL_TYPE_SET: // char[]
|
493
505
|
case MYSQL_TYPE_ENUM: // char[]
|
494
506
|
case MYSQL_TYPE_GEOMETRY: // char[]
|
@@ -511,7 +523,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
|
|
511
523
|
return rowVal;
|
512
524
|
}
|
513
525
|
|
514
|
-
|
515
526
|
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
516
527
|
{
|
517
528
|
VALUE rowVal;
|
@@ -536,16 +547,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
|
|
536
547
|
return Qnil;
|
537
548
|
}
|
538
549
|
|
550
|
+
if (wrapper->fields == Qnil) {
|
551
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
552
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
553
|
+
}
|
539
554
|
if (args->asArray) {
|
540
555
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
541
556
|
} else {
|
542
557
|
rowVal = rb_hash_new();
|
543
558
|
}
|
544
559
|
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
545
|
-
if (wrapper->fields == Qnil) {
|
546
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
547
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
548
|
-
}
|
549
560
|
|
550
561
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
551
562
|
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
@@ -829,7 +840,9 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
829
840
|
|
830
841
|
if (row == Qnil) {
|
831
842
|
/* we don't need the mysql C dataset around anymore, peace it */
|
832
|
-
|
843
|
+
if (args->cacheRows) {
|
844
|
+
rb_mysql_result_free_result(wrapper);
|
845
|
+
}
|
833
846
|
return Qnil;
|
834
847
|
}
|
835
848
|
|
@@ -837,7 +850,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
|
|
837
850
|
rb_yield(row);
|
838
851
|
}
|
839
852
|
}
|
840
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
853
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
|
841
854
|
/* we don't need the mysql C dataset around anymore, peace it */
|
842
855
|
rb_mysql_result_free_result(wrapper);
|
843
856
|
}
|
@@ -881,6 +894,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
881
894
|
|
882
895
|
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
883
896
|
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
897
|
+
cacheRows = 1;
|
884
898
|
}
|
885
899
|
|
886
900
|
if (wrapper->stmt_wrapper && !cast) {
|
@@ -908,12 +922,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
908
922
|
app_timezone = Qnil;
|
909
923
|
}
|
910
924
|
|
911
|
-
if (wrapper->
|
925
|
+
if (wrapper->rows == Qnil && !wrapper->is_streaming) {
|
912
926
|
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
913
|
-
|
914
|
-
|
915
|
-
|
927
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
928
|
+
} else if (wrapper->rows && !cacheRows) {
|
929
|
+
if (wrapper->resultFreed) {
|
930
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
916
931
|
}
|
932
|
+
mysql_data_seek(wrapper->result, 0);
|
933
|
+
wrapper->lastRowProcessed = 0;
|
917
934
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
918
935
|
}
|
919
936
|
|
@@ -1007,6 +1024,7 @@ void init_mysql2_result() {
|
|
1007
1024
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
1008
1025
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
1009
1026
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
1027
|
+
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
1010
1028
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
1011
1029
|
rb_define_alias(cMysql2Result, "size", "count");
|
1012
1030
|
|
data/ext/mysql2/result.h
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#ifndef MYSQL2_RESULT_H
|
2
2
|
#define MYSQL2_RESULT_H
|
3
|
+
#include <stdbool.h>
|
3
4
|
|
4
5
|
void init_mysql2_result(void);
|
5
6
|
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement);
|
@@ -21,8 +22,8 @@ typedef struct {
|
|
21
22
|
mysql_client_wrapper *client_wrapper;
|
22
23
|
/* statement result bind buffers */
|
23
24
|
MYSQL_BIND *result_buffers;
|
24
|
-
|
25
|
-
|
25
|
+
bool *is_null;
|
26
|
+
bool *error;
|
26
27
|
unsigned long *length;
|
27
28
|
} mysql2_result_wrapper;
|
28
29
|
|
data/ext/mysql2/statement.c
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
VALUE cMysql2Statement;
|
4
4
|
extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
|
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;
|
5
|
+
static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s;
|
6
|
+
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
|
7
|
+
#ifndef HAVE_RB_BIG_CMP
|
8
|
+
static ID id_cmp;
|
9
|
+
#endif
|
7
10
|
|
8
11
|
#define GET_STATEMENT(self) \
|
9
12
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -11,7 +14,6 @@ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, inter
|
|
11
14
|
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
|
12
15
|
if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
|
13
16
|
|
14
|
-
|
15
17
|
static void rb_mysql_stmt_mark(void * ptr) {
|
16
18
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
17
19
|
if (!stmt_wrapper) return;
|
@@ -42,7 +44,6 @@ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
|
|
42
44
|
}
|
43
45
|
}
|
44
46
|
|
45
|
-
|
46
47
|
void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
47
48
|
VALUE e;
|
48
49
|
GET_CLIENT(stmt_wrapper->client);
|
@@ -71,7 +72,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
71
72
|
rb_exc_raise(e);
|
72
73
|
}
|
73
74
|
|
74
|
-
|
75
75
|
/*
|
76
76
|
* used to pass all arguments to mysql_stmt_prepare while inside
|
77
77
|
* nogvl_prepare_statement_args
|
@@ -124,7 +124,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
124
124
|
|
125
125
|
// set STMT_ATTR_UPDATE_MAX_LENGTH attr
|
126
126
|
{
|
127
|
-
|
127
|
+
bool truth = 1;
|
128
128
|
if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
|
129
129
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
|
130
130
|
}
|
@@ -180,14 +180,16 @@ static void *nogvl_execute(void *ptr) {
|
|
180
180
|
}
|
181
181
|
}
|
182
182
|
|
183
|
-
static void *
|
184
|
-
|
183
|
+
static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
|
184
|
+
unsigned long length;
|
185
185
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
186
|
+
bind_buffer->buffer = RSTRING_PTR(string);
|
187
|
+
|
188
|
+
length = RSTRING_LEN(string);
|
189
|
+
bind_buffer->buffer_length = length;
|
190
|
+
*length_buffer = length;
|
191
|
+
|
192
|
+
bind_buffer->length = length_buffer;
|
191
193
|
}
|
192
194
|
|
193
195
|
/* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means
|
@@ -204,6 +206,50 @@ static void *nogvl_stmt_store_result(void *ptr) {
|
|
204
206
|
xfree(length_buffers); \
|
205
207
|
}
|
206
208
|
|
209
|
+
/* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
|
210
|
+
static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
211
|
+
{
|
212
|
+
unsigned LONG_LONG num;
|
213
|
+
size_t len;
|
214
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
215
|
+
int nlz_bits = 0;
|
216
|
+
len = rb_absint_size(bignum, &nlz_bits);
|
217
|
+
#else
|
218
|
+
len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS;
|
219
|
+
#endif
|
220
|
+
if (len > sizeof(LONG_LONG)) goto overflow;
|
221
|
+
if (RBIGNUM_POSITIVE_P(bignum)) {
|
222
|
+
num = rb_big2ull(bignum);
|
223
|
+
if (num > LLONG_MAX)
|
224
|
+
goto overflow;
|
225
|
+
*ptr = num;
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
if (len == 8 &&
|
229
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
230
|
+
nlz_bits == 0 &&
|
231
|
+
#endif
|
232
|
+
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
233
|
+
/* Optimized to avoid object allocation for Ruby 2.1+
|
234
|
+
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
235
|
+
*/
|
236
|
+
!rb_absint_singlebit_p(bignum)
|
237
|
+
#elif defined(HAVE_RB_BIG_CMP)
|
238
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
239
|
+
#else
|
240
|
+
/* Ruby 1.8.7 and REE doesn't have rb_big_cmp */
|
241
|
+
rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
242
|
+
#endif
|
243
|
+
) {
|
244
|
+
goto overflow;
|
245
|
+
}
|
246
|
+
*ptr = rb_big2ll(bignum);
|
247
|
+
}
|
248
|
+
return 0;
|
249
|
+
overflow:
|
250
|
+
return 1;
|
251
|
+
}
|
252
|
+
|
207
253
|
/* call-seq: stmt.execute
|
208
254
|
*
|
209
255
|
* Executes the current prepared statement, returns +result+.
|
@@ -265,9 +311,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
265
311
|
#endif
|
266
312
|
break;
|
267
313
|
case T_BIGNUM:
|
268
|
-
|
269
|
-
|
270
|
-
|
314
|
+
{
|
315
|
+
LONG_LONG num;
|
316
|
+
if (my_big2ll(argv[i], &num) == 0) {
|
317
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
318
|
+
bind_buffers[i].buffer = xmalloc(sizeof(long long int));
|
319
|
+
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
320
|
+
} else {
|
321
|
+
/* The bignum was larger than we can fit in LONG_LONG, send it as a string */
|
322
|
+
VALUE rb_val_as_string = rb_big2str(argv[i], 10);
|
323
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
324
|
+
params_enc[i] = rb_val_as_string;
|
325
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
326
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
327
|
+
#endif
|
328
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
|
+
}
|
330
|
+
}
|
271
331
|
break;
|
272
332
|
case T_FLOAT:
|
273
333
|
bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
|
@@ -275,17 +335,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
275
335
|
*(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
|
276
336
|
break;
|
277
337
|
case T_STRING:
|
278
|
-
|
279
|
-
|
338
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
339
|
+
|
340
|
+
params_enc[i] = argv[i];
|
280
341
|
#ifdef HAVE_RUBY_ENCODING_H
|
281
|
-
|
342
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
282
343
|
#endif
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
344
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
345
|
+
break;
|
346
|
+
case T_TRUE:
|
347
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
348
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
349
|
+
*(signed char*)(bind_buffers[i].buffer) = 1;
|
350
|
+
break;
|
351
|
+
case T_FALSE:
|
352
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
353
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
354
|
+
*(signed char*)(bind_buffers[i].buffer) = 0;
|
289
355
|
break;
|
290
356
|
default:
|
291
357
|
// TODO: what Ruby type should support MYSQL_TYPE_TIME
|
@@ -298,7 +364,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
298
364
|
|
299
365
|
memset(&t, 0, sizeof(MYSQL_TIME));
|
300
366
|
t.neg = 0;
|
301
|
-
|
367
|
+
|
368
|
+
if (CLASS_OF(argv[i]) == rb_cTime) {
|
369
|
+
t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
|
370
|
+
} else if (CLASS_OF(argv[i]) == cDateTime) {
|
371
|
+
t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000;
|
372
|
+
}
|
373
|
+
|
302
374
|
t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
|
303
375
|
t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
|
304
376
|
t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
|
@@ -324,6 +396,19 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
324
396
|
*(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
|
325
397
|
} else if (CLASS_OF(argv[i]) == cBigDecimal) {
|
326
398
|
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
399
|
+
|
400
|
+
// DECIMAL are represented with the "string representation of the
|
401
|
+
// original server-side value", see
|
402
|
+
// https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html
|
403
|
+
// This should be independent of the locale used both on the server
|
404
|
+
// and the client side.
|
405
|
+
VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
|
406
|
+
|
407
|
+
params_enc[i] = rb_val_as_string;
|
408
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
409
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
410
|
+
#endif
|
411
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
327
412
|
}
|
328
413
|
break;
|
329
414
|
}
|
@@ -347,8 +432,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
347
432
|
if (metadata == NULL) {
|
348
433
|
if (mysql_stmt_errno(stmt) != 0) {
|
349
434
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
350
|
-
|
351
|
-
MARK_CONN_INACTIVE(stmt_wrapper->client);
|
435
|
+
wrapper->active_thread = Qnil;
|
352
436
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
353
437
|
}
|
354
438
|
// no data and no error, so query was not a SELECT
|
@@ -362,11 +446,11 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
362
446
|
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
363
447
|
if (!is_streaming) {
|
364
448
|
// recieve the whole result set from the server
|
365
|
-
if (
|
449
|
+
if (mysql_stmt_store_result(stmt)) {
|
366
450
|
mysql_free_result(metadata);
|
367
451
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
368
452
|
}
|
369
|
-
|
453
|
+
wrapper->active_thread = Qnil;
|
370
454
|
}
|
371
455
|
|
372
456
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
@@ -394,6 +478,7 @@ static VALUE fields(VALUE self) {
|
|
394
478
|
rb_encoding *default_internal_enc, *conn_enc;
|
395
479
|
#endif
|
396
480
|
GET_STATEMENT(self);
|
481
|
+
GET_CLIENT(stmt_wrapper->client);
|
397
482
|
stmt = stmt_wrapper->stmt;
|
398
483
|
|
399
484
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -404,12 +489,22 @@ static VALUE fields(VALUE self) {
|
|
404
489
|
}
|
405
490
|
#endif
|
406
491
|
|
407
|
-
metadata
|
492
|
+
metadata = mysql_stmt_result_metadata(stmt);
|
493
|
+
if (metadata == NULL) {
|
494
|
+
if (mysql_stmt_errno(stmt) != 0) {
|
495
|
+
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
496
|
+
wrapper->active_thread = Qnil;
|
497
|
+
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
498
|
+
}
|
499
|
+
// no data and no error, so query was not a SELECT
|
500
|
+
return Qnil;
|
501
|
+
}
|
502
|
+
|
408
503
|
fields = mysql_fetch_fields(metadata);
|
409
504
|
field_count = mysql_stmt_field_count(stmt);
|
410
505
|
field_list = rb_ary_new2((long)field_count);
|
411
506
|
|
412
|
-
for(i = 0; i < field_count; i++) {
|
507
|
+
for (i = 0; i < field_count; i++) {
|
413
508
|
VALUE rb_field;
|
414
509
|
|
415
510
|
rb_field = rb_str_new(fields[i].name, fields[i].name_length);
|
@@ -484,6 +579,7 @@ void init_mysql2_statement() {
|
|
484
579
|
intern_new_with_args = rb_intern("new_with_args");
|
485
580
|
intern_each = rb_intern("each");
|
486
581
|
|
582
|
+
intern_sec_fraction = rb_intern("sec_fraction");
|
487
583
|
intern_usec = rb_intern("usec");
|
488
584
|
intern_sec = rb_intern("sec");
|
489
585
|
intern_min = rb_intern("min");
|
@@ -491,4 +587,9 @@ void init_mysql2_statement() {
|
|
491
587
|
intern_day = rb_intern("day");
|
492
588
|
intern_month = rb_intern("month");
|
493
589
|
intern_year = rb_intern("year");
|
590
|
+
|
591
|
+
intern_to_s = rb_intern("to_s");
|
592
|
+
#ifndef HAVE_RB_BIG_CMP
|
593
|
+
id_cmp = rb_intern("<=>");
|
594
|
+
#endif
|
494
595
|
}
|
data/lib/mysql2/client.rb
CHANGED
@@ -19,6 +19,7 @@ module Mysql2
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
|
+
fail Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
22
23
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
23
24
|
@read_timeout = nil
|
24
25
|
@query_options = self.class.default_query_options.dup
|
@@ -30,13 +31,13 @@ module Mysql2
|
|
30
31
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
31
32
|
|
32
33
|
# 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|
|
34
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close, :enable_cleartext_plugin].each do |key|
|
34
35
|
next unless opts.key?(key)
|
35
36
|
case key
|
36
|
-
when :reconnect, :local_infile, :secure_auth
|
37
|
+
when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
|
37
38
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
38
39
|
when :connect_timeout, :read_timeout, :write_timeout
|
39
|
-
send(:"#{key}=", opts[key].
|
40
|
+
send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
|
40
41
|
else
|
41
42
|
send(:"#{key}=", opts[key])
|
42
43
|
end
|
@@ -46,7 +47,8 @@ module Mysql2
|
|
46
47
|
self.charset_name = opts[:encoding] || 'utf8'
|
47
48
|
|
48
49
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
49
|
-
ssl_set(*ssl_options) if ssl_options.any?
|
50
|
+
ssl_set(*ssl_options) if ssl_options.any? || opts.key?(:sslverify)
|
51
|
+
self.ssl_mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
50
52
|
|
51
53
|
case opts[:flags]
|
52
54
|
when Array
|
@@ -60,11 +62,11 @@ module Mysql2
|
|
60
62
|
end
|
61
63
|
|
62
64
|
# SSL verify is a connection flag rather than a mysql_ssl_set option
|
63
|
-
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
65
|
+
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
64
66
|
|
65
67
|
if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) }
|
66
68
|
warn "============= WARNING FROM mysql2 ============="
|
67
|
-
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be
|
69
|
+
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future."
|
68
70
|
warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options."
|
69
71
|
warn "============= END WARNING FROM mysql2 ========="
|
70
72
|
end
|
@@ -87,6 +89,17 @@ module Mysql2
|
|
87
89
|
connect user, pass, host, port, database, socket, flags
|
88
90
|
end
|
89
91
|
|
92
|
+
def parse_ssl_mode(mode)
|
93
|
+
m = mode.to_s.upcase
|
94
|
+
if m.start_with?('SSL_MODE_')
|
95
|
+
return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
|
96
|
+
else
|
97
|
+
x = 'SSL_MODE_' + m
|
98
|
+
return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
|
99
|
+
end
|
100
|
+
warn "Unknown MySQL ssl_mode flag: #{mode}"
|
101
|
+
end
|
102
|
+
|
90
103
|
def parse_flags_array(flags, initial = 0)
|
91
104
|
flags.reduce(initial) do |memo, f|
|
92
105
|
fneg = f.start_with?('-') ? f[1..-1] : nil
|
data/lib/mysql2/version.rb
CHANGED
data/spec/em/em_spec.rb
CHANGED
@@ -70,6 +70,7 @@ begin
|
|
70
70
|
let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
|
71
71
|
let(:error) { StandardError.new('some error') }
|
72
72
|
before { allow(client).to receive(:async_result).and_raise(error) }
|
73
|
+
after { client.close }
|
73
74
|
|
74
75
|
it "should swallow exceptions raised in by the client" do
|
75
76
|
errors = []
|