mysql2 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +90 -52
- data/examples/eventmachine.rb +0 -2
- data/examples/threaded.rb +2 -4
- data/ext/mysql2/client.c +260 -76
- data/ext/mysql2/client.h +2 -47
- data/ext/mysql2/extconf.rb +44 -18
- data/ext/mysql2/mysql2_ext.c +2 -1
- data/ext/mysql2/mysql2_ext.h +8 -8
- data/ext/mysql2/result.c +23 -74
- data/ext/mysql2/statement.c +139 -63
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +50 -31
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +49 -20
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -9
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +14 -15
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +6 -6
- data/spec/mysql2/client_spec.rb +318 -237
- data/spec/mysql2/error_spec.rb +4 -10
- data/spec/mysql2/result_spec.rb +124 -158
- data/spec/mysql2/statement_spec.rb +185 -180
- data/spec/spec_helper.rb +79 -61
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +2 -2
- data/support/ruby_enc_to_mysql.rb +5 -5
- metadata +16 -14
data/ext/mysql2/statement.c
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
-
VALUE
|
4
|
-
|
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
|
3
|
+
extern VALUE mMysql2, cMysql2Error;
|
4
|
+
static VALUE cMysql2Statement, cBigDecimal, cDateTime, cDate;
|
5
|
+
static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_merge_bang;
|
6
|
+
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
|
7
7
|
|
8
8
|
#define GET_STATEMENT(self) \
|
9
9
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -18,7 +18,7 @@ static void rb_mysql_stmt_mark(void * ptr) {
|
|
18
18
|
rb_gc_mark(stmt_wrapper->client);
|
19
19
|
}
|
20
20
|
|
21
|
-
static void *nogvl_stmt_close(void *
|
21
|
+
static void *nogvl_stmt_close(void *ptr) {
|
22
22
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
23
23
|
if (stmt_wrapper->stmt) {
|
24
24
|
mysql_stmt_close(stmt_wrapper->stmt);
|
@@ -27,7 +27,7 @@ static void *nogvl_stmt_close(void * ptr) {
|
|
27
27
|
return NULL;
|
28
28
|
}
|
29
29
|
|
30
|
-
static void rb_mysql_stmt_free(void *
|
30
|
+
static void rb_mysql_stmt_free(void *ptr) {
|
31
31
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
32
32
|
decr_mysql2_stmt(stmt_wrapper);
|
33
33
|
}
|
@@ -47,7 +47,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
47
47
|
VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
|
48
48
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
|
49
49
|
|
50
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
51
50
|
rb_encoding *conn_enc;
|
52
51
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
53
52
|
|
@@ -59,7 +58,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
59
58
|
rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
|
60
59
|
rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
|
61
60
|
}
|
62
|
-
#endif
|
63
61
|
|
64
62
|
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
65
63
|
rb_error_msg,
|
@@ -93,9 +91,7 @@ static void *nogvl_prepare_statement(void *ptr) {
|
|
93
91
|
VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
94
92
|
mysql_stmt_wrapper *stmt_wrapper;
|
95
93
|
VALUE rb_stmt;
|
96
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
97
94
|
rb_encoding *conn_enc;
|
98
|
-
#endif
|
99
95
|
|
100
96
|
Check_Type(sql, T_STRING);
|
101
97
|
|
@@ -111,9 +107,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
111
107
|
{
|
112
108
|
GET_CLIENT(rb_client);
|
113
109
|
stmt_wrapper->stmt = mysql_stmt_init(wrapper->client);
|
114
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
115
110
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
116
|
-
#endif
|
117
111
|
}
|
118
112
|
if (stmt_wrapper->stmt == NULL) {
|
119
113
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: out of memory");
|
@@ -131,11 +125,8 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
131
125
|
{
|
132
126
|
struct nogvl_prepare_statement_args args;
|
133
127
|
args.stmt = stmt_wrapper->stmt;
|
134
|
-
args.sql = sql;
|
135
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
136
128
|
// ensure the string is in the encoding the connection is expecting
|
137
|
-
args.sql = rb_str_export_to_enc(
|
138
|
-
#endif
|
129
|
+
args.sql = rb_str_export_to_enc(sql, conn_enc);
|
139
130
|
args.sql_ptr = RSTRING_PTR(sql);
|
140
131
|
args.sql_len = RSTRING_LEN(sql);
|
141
132
|
|
@@ -151,7 +142,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
151
142
|
*
|
152
143
|
* Returns the number of parameters the prepared statement expects.
|
153
144
|
*/
|
154
|
-
static VALUE
|
145
|
+
static VALUE rb_mysql_stmt_param_count(VALUE self) {
|
155
146
|
GET_STATEMENT(self);
|
156
147
|
|
157
148
|
return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt));
|
@@ -161,13 +152,13 @@ static VALUE param_count(VALUE self) {
|
|
161
152
|
*
|
162
153
|
* Returns the number of fields the prepared statement returns.
|
163
154
|
*/
|
164
|
-
static VALUE
|
155
|
+
static VALUE rb_mysql_stmt_field_count(VALUE self) {
|
165
156
|
GET_STATEMENT(self);
|
166
157
|
|
167
158
|
return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt));
|
168
159
|
}
|
169
160
|
|
170
|
-
static void *
|
161
|
+
static void *nogvl_stmt_execute(void *ptr) {
|
171
162
|
MYSQL_STMT *stmt = ptr;
|
172
163
|
|
173
164
|
if (mysql_stmt_execute(stmt)) {
|
@@ -180,7 +171,6 @@ static void *nogvl_execute(void *ptr) {
|
|
180
171
|
static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
|
181
172
|
unsigned long length;
|
182
173
|
|
183
|
-
bind_buffer->buffer_type = MYSQL_TYPE_STRING;
|
184
174
|
bind_buffer->buffer = RSTRING_PTR(string);
|
185
175
|
|
186
176
|
length = RSTRING_LEN(string);
|
@@ -194,7 +184,7 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
194
184
|
* the buffer is a Ruby string pointer and not our memory to manage.
|
195
185
|
*/
|
196
186
|
#define FREE_BINDS \
|
197
|
-
for (i = 0; i <
|
187
|
+
for (i = 0; i < bind_count; i++) { \
|
198
188
|
if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \
|
199
189
|
xfree(bind_buffers[i].buffer); \
|
200
190
|
} \
|
@@ -204,48 +194,94 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
204
194
|
xfree(length_buffers); \
|
205
195
|
}
|
206
196
|
|
197
|
+
/* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
|
198
|
+
static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
199
|
+
{
|
200
|
+
unsigned LONG_LONG num;
|
201
|
+
size_t len;
|
202
|
+
// rb_absint_size was added in 2.1.0. See:
|
203
|
+
// https://github.com/ruby/ruby/commit/9fea875
|
204
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
205
|
+
int nlz_bits = 0;
|
206
|
+
len = rb_absint_size(bignum, &nlz_bits);
|
207
|
+
#else
|
208
|
+
len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS;
|
209
|
+
#endif
|
210
|
+
if (len > sizeof(LONG_LONG)) goto overflow;
|
211
|
+
if (RBIGNUM_POSITIVE_P(bignum)) {
|
212
|
+
num = rb_big2ull(bignum);
|
213
|
+
if (num > LLONG_MAX)
|
214
|
+
goto overflow;
|
215
|
+
*ptr = num;
|
216
|
+
}
|
217
|
+
else {
|
218
|
+
if (len == 8 &&
|
219
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
220
|
+
nlz_bits == 0 &&
|
221
|
+
#endif
|
222
|
+
// rb_absint_singlebit_p was added in 2.1.0. See:
|
223
|
+
// https://github.com/ruby/ruby/commit/e5ff9d5
|
224
|
+
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
225
|
+
/* Optimized to avoid object allocation for Ruby 2.1+
|
226
|
+
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
227
|
+
*/
|
228
|
+
!rb_absint_singlebit_p(bignum)
|
229
|
+
#else
|
230
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
231
|
+
#endif
|
232
|
+
) {
|
233
|
+
goto overflow;
|
234
|
+
}
|
235
|
+
*ptr = rb_big2ll(bignum);
|
236
|
+
}
|
237
|
+
return 0;
|
238
|
+
overflow:
|
239
|
+
return 1;
|
240
|
+
}
|
241
|
+
|
207
242
|
/* call-seq: stmt.execute
|
208
243
|
*
|
209
244
|
* Executes the current prepared statement, returns +result+.
|
210
245
|
*/
|
211
|
-
static VALUE
|
246
|
+
static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
212
247
|
MYSQL_BIND *bind_buffers = NULL;
|
213
248
|
unsigned long *length_buffers = NULL;
|
214
249
|
unsigned long bind_count;
|
215
|
-
long i;
|
250
|
+
unsigned long i;
|
216
251
|
MYSQL_STMT *stmt;
|
217
252
|
MYSQL_RES *metadata;
|
253
|
+
VALUE opts;
|
218
254
|
VALUE current;
|
219
255
|
VALUE resultObj;
|
220
|
-
VALUE *params_enc;
|
256
|
+
VALUE *params_enc = NULL;
|
221
257
|
int is_streaming;
|
222
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
223
258
|
rb_encoding *conn_enc;
|
224
|
-
#endif
|
225
259
|
|
226
260
|
GET_STATEMENT(self);
|
227
261
|
GET_CLIENT(stmt_wrapper->client);
|
228
262
|
|
229
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
230
263
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
231
|
-
#endif
|
232
|
-
|
233
|
-
/* Scratch space for string encoding exports, allocate on the stack. */
|
234
|
-
params_enc = alloca(sizeof(VALUE) * argc);
|
235
264
|
|
236
265
|
stmt = stmt_wrapper->stmt;
|
237
|
-
|
238
266
|
bind_count = mysql_stmt_param_count(stmt);
|
239
|
-
|
240
|
-
|
267
|
+
|
268
|
+
// Get count of ordinary arguments, and extract hash opts/keyword arguments
|
269
|
+
// Use a local scope to avoid leaking the temporary count variable
|
270
|
+
{
|
271
|
+
int c = rb_scan_args(argc, argv, "*:", NULL, &opts);
|
272
|
+
if (c != (long)bind_count) {
|
273
|
+
rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c);
|
274
|
+
}
|
241
275
|
}
|
242
276
|
|
243
277
|
// setup any bind variables in the query
|
244
278
|
if (bind_count > 0) {
|
279
|
+
// Scratch space for string encoding exports, allocate on the stack
|
280
|
+
params_enc = alloca(sizeof(VALUE) * bind_count);
|
245
281
|
bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND));
|
246
282
|
length_buffers = xcalloc(bind_count, sizeof(unsigned long));
|
247
283
|
|
248
|
-
for (i = 0; i <
|
284
|
+
for (i = 0; i < bind_count; i++) {
|
249
285
|
bind_buffers[i].buffer = NULL;
|
250
286
|
params_enc[i] = Qnil;
|
251
287
|
|
@@ -265,9 +301,19 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
265
301
|
#endif
|
266
302
|
break;
|
267
303
|
case T_BIGNUM:
|
268
|
-
|
269
|
-
|
270
|
-
|
304
|
+
{
|
305
|
+
LONG_LONG num;
|
306
|
+
if (my_big2ll(argv[i], &num) == 0) {
|
307
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
308
|
+
bind_buffers[i].buffer = xmalloc(sizeof(long long int));
|
309
|
+
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
310
|
+
} else {
|
311
|
+
/* The bignum was larger than we can fit in LONG_LONG, send it as a string */
|
312
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
313
|
+
params_enc[i] = rb_str_export_to_enc(rb_big2str(argv[i], 10), conn_enc);
|
314
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
315
|
+
}
|
316
|
+
}
|
271
317
|
break;
|
272
318
|
case T_FLOAT:
|
273
319
|
bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
|
@@ -275,13 +321,21 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
275
321
|
*(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
|
276
322
|
break;
|
277
323
|
case T_STRING:
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
324
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
325
|
+
|
326
|
+
params_enc[i] = argv[i];
|
327
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
328
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
|
+
break;
|
330
|
+
case T_TRUE:
|
331
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
332
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
333
|
+
*(signed char*)(bind_buffers[i].buffer) = 1;
|
334
|
+
break;
|
335
|
+
case T_FALSE:
|
336
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
|
337
|
+
bind_buffers[i].buffer = xmalloc(sizeof(signed char));
|
338
|
+
*(signed char*)(bind_buffers[i].buffer) = 0;
|
285
339
|
break;
|
286
340
|
default:
|
287
341
|
// TODO: what Ruby type should support MYSQL_TYPE_TIME
|
@@ -294,7 +348,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
294
348
|
|
295
349
|
memset(&t, 0, sizeof(MYSQL_TIME));
|
296
350
|
t.neg = 0;
|
297
|
-
|
351
|
+
|
352
|
+
if (CLASS_OF(argv[i]) == rb_cTime) {
|
353
|
+
t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
|
354
|
+
} else if (CLASS_OF(argv[i]) == cDateTime) {
|
355
|
+
t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000;
|
356
|
+
}
|
357
|
+
|
298
358
|
t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
|
299
359
|
t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
|
300
360
|
t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
|
@@ -329,9 +389,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
329
389
|
VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
|
330
390
|
|
331
391
|
params_enc[i] = rb_val_as_string;
|
332
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
333
392
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
334
|
-
#endif
|
335
393
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
336
394
|
}
|
337
395
|
break;
|
@@ -345,7 +403,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
345
403
|
}
|
346
404
|
}
|
347
405
|
|
348
|
-
if ((VALUE)rb_thread_call_without_gvl(
|
406
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
|
349
407
|
FREE_BINDS;
|
350
408
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
351
409
|
}
|
@@ -363,10 +421,16 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
363
421
|
return Qnil;
|
364
422
|
}
|
365
423
|
|
424
|
+
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
366
425
|
current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
|
367
426
|
(void)RB_GC_GUARD(current);
|
368
427
|
Check_Type(current, T_HASH);
|
369
428
|
|
429
|
+
// Merge in hash opts/keyword arguments
|
430
|
+
if (!NIL_P(opts)) {
|
431
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
432
|
+
}
|
433
|
+
|
370
434
|
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
371
435
|
if (!is_streaming) {
|
372
436
|
// recieve the whole result set from the server
|
@@ -379,6 +443,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
379
443
|
|
380
444
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
381
445
|
|
446
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
447
|
+
|
382
448
|
if (!is_streaming) {
|
383
449
|
// cache all result
|
384
450
|
rb_funcall(resultObj, intern_each, 0);
|
@@ -391,42 +457,47 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
391
457
|
*
|
392
458
|
* Returns a list of fields that will be returned by this statement.
|
393
459
|
*/
|
394
|
-
static VALUE
|
460
|
+
static VALUE rb_mysql_stmt_fields(VALUE self) {
|
395
461
|
MYSQL_FIELD *fields;
|
396
462
|
MYSQL_RES *metadata;
|
397
463
|
unsigned int field_count;
|
398
464
|
unsigned int i;
|
399
465
|
VALUE field_list;
|
400
466
|
MYSQL_STMT* stmt;
|
401
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
402
467
|
rb_encoding *default_internal_enc, *conn_enc;
|
403
|
-
#endif
|
404
468
|
GET_STATEMENT(self);
|
469
|
+
GET_CLIENT(stmt_wrapper->client);
|
405
470
|
stmt = stmt_wrapper->stmt;
|
406
471
|
|
407
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
408
472
|
default_internal_enc = rb_default_internal_encoding();
|
409
473
|
{
|
410
474
|
GET_CLIENT(stmt_wrapper->client);
|
411
475
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
412
476
|
}
|
413
|
-
#endif
|
414
477
|
|
415
|
-
metadata
|
478
|
+
metadata = mysql_stmt_result_metadata(stmt);
|
479
|
+
if (metadata == NULL) {
|
480
|
+
if (mysql_stmt_errno(stmt) != 0) {
|
481
|
+
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
482
|
+
wrapper->active_thread = Qnil;
|
483
|
+
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
484
|
+
}
|
485
|
+
// no data and no error, so query was not a SELECT
|
486
|
+
return Qnil;
|
487
|
+
}
|
488
|
+
|
416
489
|
fields = mysql_fetch_fields(metadata);
|
417
490
|
field_count = mysql_stmt_field_count(stmt);
|
418
491
|
field_list = rb_ary_new2((long)field_count);
|
419
492
|
|
420
|
-
for(i = 0; i < field_count; i++) {
|
493
|
+
for (i = 0; i < field_count; i++) {
|
421
494
|
VALUE rb_field;
|
422
495
|
|
423
496
|
rb_field = rb_str_new(fields[i].name, fields[i].name_length);
|
424
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
425
497
|
rb_enc_associate(rb_field, conn_enc);
|
426
498
|
if (default_internal_enc) {
|
427
499
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
428
500
|
}
|
429
|
-
#endif
|
430
501
|
|
431
502
|
rb_ary_store(field_list, (long)i, rb_field);
|
432
503
|
}
|
@@ -477,12 +548,15 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
|
|
477
548
|
}
|
478
549
|
|
479
550
|
void init_mysql2_statement() {
|
480
|
-
|
551
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
552
|
+
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
553
|
+
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
481
554
|
|
482
|
-
|
483
|
-
rb_define_method(cMysql2Statement, "
|
484
|
-
rb_define_method(cMysql2Statement, "
|
485
|
-
rb_define_method(cMysql2Statement, "
|
555
|
+
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
|
556
|
+
rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
|
557
|
+
rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
|
558
|
+
rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
|
559
|
+
rb_define_method(cMysql2Statement, "fields", rb_mysql_stmt_fields, 0);
|
486
560
|
rb_define_method(cMysql2Statement, "last_id", rb_mysql_stmt_last_id, 0);
|
487
561
|
rb_define_method(cMysql2Statement, "affected_rows", rb_mysql_stmt_affected_rows, 0);
|
488
562
|
rb_define_method(cMysql2Statement, "close", rb_mysql_stmt_close, 0);
|
@@ -492,6 +566,7 @@ void init_mysql2_statement() {
|
|
492
566
|
intern_new_with_args = rb_intern("new_with_args");
|
493
567
|
intern_each = rb_intern("each");
|
494
568
|
|
569
|
+
intern_sec_fraction = rb_intern("sec_fraction");
|
495
570
|
intern_usec = rb_intern("usec");
|
496
571
|
intern_sec = rb_intern("sec");
|
497
572
|
intern_min = rb_intern("min");
|
@@ -501,4 +576,5 @@ void init_mysql2_statement() {
|
|
501
576
|
intern_year = rb_intern("year");
|
502
577
|
|
503
578
|
intern_to_s = rb_intern("to_s");
|
579
|
+
intern_merge_bang = rb_intern("merge!");
|
504
580
|
}
|
data/ext/mysql2/statement.h
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
/*
|
2
|
-
* backwards compatibility for
|
2
|
+
* backwards compatibility for Rubinius. See
|
3
|
+
* https://github.com/rubinius/rubinius/issues/3771.
|
3
4
|
*
|
4
5
|
* Ruby 1.9.3 provides this API which allows the use of ppoll() on Linux
|
5
6
|
* to minimize select() and malloc() overhead on high-numbered FDs.
|
data/lib/mysql2/client.rb
CHANGED
@@ -4,21 +4,22 @@ module Mysql2
|
|
4
4
|
|
5
5
|
def self.default_query_options
|
6
6
|
@default_query_options ||= {
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
7
|
+
as: :hash, # the type of object you want each row back as; also supports :array (an array of values)
|
8
|
+
async: false, # don't wait for a result after sending the query, you'll have to monitor the socket yourself then eventually call Mysql2::Client#async_result
|
9
|
+
cast_booleans: false, # cast tinyint(1) fields as true/false in ruby
|
10
|
+
symbolize_keys: false, # return field names as symbols instead of strings
|
11
|
+
database_timezone: :local, # timezone Mysql2 will assume datetime objects are stored in
|
12
|
+
application_timezone: nil, # timezone Mysql2 will convert to before handing the object back to the caller
|
13
|
+
cache_rows: true, # tells Mysql2 to use its internal row cache for results
|
14
|
+
connect_flags: REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION | CONNECT_ATTRS,
|
15
|
+
cast: true,
|
16
|
+
default_file: nil,
|
17
|
+
default_group: nil,
|
18
18
|
}
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
|
+
raise 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
|
-
[
|
34
|
+
%i[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, :automatic_close
|
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]) unless opts[key].nil?
|
40
|
+
send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
|
40
41
|
else
|
41
42
|
send(:"#{key}=", opts[key])
|
42
43
|
end
|
@@ -46,25 +47,26 @@ 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
|
-
case opts[:flags]
|
53
|
+
flags = case opts[:flags]
|
52
54
|
when Array
|
53
|
-
|
55
|
+
parse_flags_array(opts[:flags], @query_options[:connect_flags])
|
54
56
|
when String
|
55
|
-
|
57
|
+
parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
|
56
58
|
when Integer
|
57
|
-
|
59
|
+
@query_options[:connect_flags] | opts[:flags]
|
58
60
|
else
|
59
|
-
|
61
|
+
@query_options[:connect_flags]
|
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
|
-
if [
|
67
|
+
if %i[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
|
@@ -83,8 +85,20 @@ module Mysql2
|
|
83
85
|
port = port.to_i unless port.nil?
|
84
86
|
database = database.to_s unless database.nil?
|
85
87
|
socket = socket.to_s unless socket.nil?
|
88
|
+
conn_attrs = parse_connect_attrs(opts[:connect_attrs])
|
86
89
|
|
87
|
-
connect user, pass, host, port, database, socket, flags
|
90
|
+
connect user, pass, host, port, database, socket, flags, conn_attrs
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_ssl_mode(mode)
|
94
|
+
m = mode.to_s.upcase
|
95
|
+
if m.start_with?('SSL_MODE_')
|
96
|
+
return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
|
97
|
+
else
|
98
|
+
x = 'SSL_MODE_' + m
|
99
|
+
return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
|
100
|
+
end
|
101
|
+
warn "Unknown MySQL ssl_mode flag: #{mode}"
|
88
102
|
end
|
89
103
|
|
90
104
|
def parse_flags_array(flags, initial = 0)
|
@@ -101,14 +115,19 @@ module Mysql2
|
|
101
115
|
end
|
102
116
|
end
|
103
117
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
118
|
+
# Set default program_name in performance_schema.session_connect_attrs
|
119
|
+
# and performance_schema.session_account_connect_attrs
|
120
|
+
def parse_connect_attrs(conn_attrs)
|
121
|
+
return {} if Mysql2::Client::CONNECT_ATTRS.zero?
|
122
|
+
conn_attrs ||= {}
|
123
|
+
conn_attrs[:program_name] ||= $PROGRAM_NAME
|
124
|
+
conn_attrs.each_with_object({}) do |(key, value), hash|
|
125
|
+
hash[key.to_s] = value.to_s
|
109
126
|
end
|
110
|
-
|
111
|
-
|
127
|
+
end
|
128
|
+
|
129
|
+
def query(sql, options = {})
|
130
|
+
Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_CLASS => :never) do
|
112
131
|
_query(sql, @query_options.merge(options))
|
113
132
|
end
|
114
133
|
end
|
data/lib/mysql2/em.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'eventmachine'
|
4
2
|
require 'mysql2'
|
5
3
|
|
@@ -17,7 +15,7 @@ module Mysql2
|
|
17
15
|
detach
|
18
16
|
begin
|
19
17
|
result = @client.async_result
|
20
|
-
rescue => e
|
18
|
+
rescue StandardError => e
|
21
19
|
@deferable.fail(e)
|
22
20
|
else
|
23
21
|
@deferable.succeed(result)
|
@@ -41,7 +39,7 @@ module Mysql2
|
|
41
39
|
|
42
40
|
def query(sql, opts = {})
|
43
41
|
if ::EM.reactor_running?
|
44
|
-
super(sql, opts.merge(:
|
42
|
+
super(sql, opts.merge(async: true))
|
45
43
|
deferable = ::EM::DefaultDeferrable.new
|
46
44
|
@watch = ::EM.watch(socket, Watcher, self, deferable)
|
47
45
|
@watch.notify_readable = true
|