mysql2 0.4.10 → 0.5.6
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/README.md +167 -48
- data/ext/mysql2/client.c +335 -108
- data/ext/mysql2/client.h +10 -41
- data/ext/mysql2/extconf.rb +84 -26
- data/ext/mysql2/mysql2_ext.c +8 -2
- data/ext/mysql2/mysql2_ext.h +21 -4
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -55
- data/ext/mysql2/mysql_enc_to_ruby.h +79 -3
- data/ext/mysql2/result.c +298 -92
- data/ext/mysql2/result.h +3 -3
- data/ext/mysql2/statement.c +137 -81
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +55 -28
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +52 -22
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -11
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +19 -15
- data/support/3A79BD29.asc +49 -0
- data/support/5072E1F5.asc +5 -5
- data/support/C74CD1D8.asc +104 -0
- data/support/mysql_enc_to_ruby.rb +9 -3
- data/support/ruby_enc_to_mysql.rb +8 -5
- metadata +19 -62
- data/examples/eventmachine.rb +0 -21
- data/examples/threaded.rb +0 -18
- data/spec/configuration.yml.example +0 -11
- data/spec/em/em_spec.rb +0 -136
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -1039
- data/spec/mysql2/error_spec.rb +0 -82
- data/spec/mysql2/result_spec.rb +0 -545
- data/spec/mysql2/statement_spec.rb +0 -776
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -108
- data/spec/ssl/ca-cert.pem +0 -17
- data/spec/ssl/ca-key.pem +0 -27
- data/spec/ssl/ca.cnf +0 -22
- data/spec/ssl/cert.cnf +0 -22
- data/spec/ssl/client-cert.pem +0 -17
- data/spec/ssl/client-key.pem +0 -27
- data/spec/ssl/client-req.pem +0 -15
- data/spec/ssl/gen_certs.sh +0 -48
- data/spec/ssl/pkcs8-client-key.pem +0 -28
- data/spec/ssl/pkcs8-server-key.pem +0 -28
- data/spec/ssl/server-cert.pem +0 -17
- data/spec/ssl/server-key.pem +0 -27
- data/spec/ssl/server-req.pem +0 -15
- data/spec/test_data +0 -1
data/ext/mysql2/statement.c
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
-
VALUE
|
4
|
-
|
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
|
-
|
8
|
-
|
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
|
+
intern_query_options;
|
8
|
+
|
9
|
+
#ifndef NEW_TYPEDDATA_WRAPPER
|
10
|
+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
|
9
11
|
#endif
|
10
12
|
|
11
13
|
#define GET_STATEMENT(self) \
|
12
14
|
mysql_stmt_wrapper *stmt_wrapper; \
|
13
|
-
|
15
|
+
TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \
|
14
16
|
if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
|
15
17
|
if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
|
16
18
|
|
@@ -18,10 +20,46 @@ static void rb_mysql_stmt_mark(void * ptr) {
|
|
18
20
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
19
21
|
if (!stmt_wrapper) return;
|
20
22
|
|
21
|
-
|
23
|
+
rb_gc_mark_movable(stmt_wrapper->client);
|
22
24
|
}
|
23
25
|
|
24
|
-
static void
|
26
|
+
static void rb_mysql_stmt_free(void *ptr) {
|
27
|
+
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
28
|
+
decr_mysql2_stmt(stmt_wrapper);
|
29
|
+
}
|
30
|
+
|
31
|
+
static size_t rb_mysql_stmt_memsize(const void * ptr) {
|
32
|
+
const mysql_stmt_wrapper *stmt_wrapper = ptr;
|
33
|
+
return sizeof(*stmt_wrapper);
|
34
|
+
}
|
35
|
+
|
36
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
37
|
+
static void rb_mysql_stmt_compact(void * ptr) {
|
38
|
+
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
39
|
+
if (!stmt_wrapper) return;
|
40
|
+
|
41
|
+
rb_mysql2_gc_location(stmt_wrapper->client);
|
42
|
+
}
|
43
|
+
#endif
|
44
|
+
|
45
|
+
static const rb_data_type_t rb_mysql_statement_type = {
|
46
|
+
"rb_mysql_statement",
|
47
|
+
{
|
48
|
+
rb_mysql_stmt_mark,
|
49
|
+
rb_mysql_stmt_free,
|
50
|
+
rb_mysql_stmt_memsize,
|
51
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
52
|
+
rb_mysql_stmt_compact,
|
53
|
+
#endif
|
54
|
+
},
|
55
|
+
0,
|
56
|
+
0,
|
57
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
58
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
59
|
+
#endif
|
60
|
+
};
|
61
|
+
|
62
|
+
static void *nogvl_stmt_close(void *ptr) {
|
25
63
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
26
64
|
if (stmt_wrapper->stmt) {
|
27
65
|
mysql_stmt_close(stmt_wrapper->stmt);
|
@@ -30,11 +68,6 @@ static void *nogvl_stmt_close(void * ptr) {
|
|
30
68
|
return NULL;
|
31
69
|
}
|
32
70
|
|
33
|
-
static void rb_mysql_stmt_free(void * ptr) {
|
34
|
-
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
35
|
-
decr_mysql2_stmt(stmt_wrapper);
|
36
|
-
}
|
37
|
-
|
38
71
|
void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
|
39
72
|
stmt_wrapper->refcount--;
|
40
73
|
|
@@ -48,9 +81,8 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
48
81
|
VALUE e;
|
49
82
|
GET_CLIENT(stmt_wrapper->client);
|
50
83
|
VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
|
51
|
-
VALUE rb_sql_state =
|
84
|
+
VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
|
52
85
|
|
53
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
54
86
|
rb_encoding *conn_enc;
|
55
87
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
56
88
|
|
@@ -62,7 +94,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
62
94
|
rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
|
63
95
|
rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
|
64
96
|
}
|
65
|
-
#endif
|
66
97
|
|
67
98
|
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
68
99
|
rb_error_msg,
|
@@ -96,13 +127,15 @@ static void *nogvl_prepare_statement(void *ptr) {
|
|
96
127
|
VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
97
128
|
mysql_stmt_wrapper *stmt_wrapper;
|
98
129
|
VALUE rb_stmt;
|
99
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
100
130
|
rb_encoding *conn_enc;
|
101
|
-
#endif
|
102
131
|
|
103
132
|
Check_Type(sql, T_STRING);
|
104
133
|
|
134
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
135
|
+
rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
|
136
|
+
#else
|
105
137
|
rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
|
138
|
+
#endif
|
106
139
|
{
|
107
140
|
stmt_wrapper->client = rb_client;
|
108
141
|
stmt_wrapper->refcount = 1;
|
@@ -114,9 +147,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
114
147
|
{
|
115
148
|
GET_CLIENT(rb_client);
|
116
149
|
stmt_wrapper->stmt = mysql_stmt_init(wrapper->client);
|
117
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
118
150
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
119
|
-
#endif
|
120
151
|
}
|
121
152
|
if (stmt_wrapper->stmt == NULL) {
|
122
153
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: out of memory");
|
@@ -124,7 +155,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
124
155
|
|
125
156
|
// set STMT_ATTR_UPDATE_MAX_LENGTH attr
|
126
157
|
{
|
127
|
-
|
158
|
+
my_bool truth = 1;
|
128
159
|
if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
|
129
160
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
|
130
161
|
}
|
@@ -134,11 +165,8 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
134
165
|
{
|
135
166
|
struct nogvl_prepare_statement_args args;
|
136
167
|
args.stmt = stmt_wrapper->stmt;
|
137
|
-
args.sql = sql;
|
138
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
139
168
|
// ensure the string is in the encoding the connection is expecting
|
140
|
-
args.sql = rb_str_export_to_enc(
|
141
|
-
#endif
|
169
|
+
args.sql = rb_str_export_to_enc(sql, conn_enc);
|
142
170
|
args.sql_ptr = RSTRING_PTR(sql);
|
143
171
|
args.sql_len = RSTRING_LEN(sql);
|
144
172
|
|
@@ -154,7 +182,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
154
182
|
*
|
155
183
|
* Returns the number of parameters the prepared statement expects.
|
156
184
|
*/
|
157
|
-
static VALUE
|
185
|
+
static VALUE rb_mysql_stmt_param_count(VALUE self) {
|
158
186
|
GET_STATEMENT(self);
|
159
187
|
|
160
188
|
return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt));
|
@@ -164,13 +192,13 @@ static VALUE param_count(VALUE self) {
|
|
164
192
|
*
|
165
193
|
* Returns the number of fields the prepared statement returns.
|
166
194
|
*/
|
167
|
-
static VALUE
|
195
|
+
static VALUE rb_mysql_stmt_field_count(VALUE self) {
|
168
196
|
GET_STATEMENT(self);
|
169
197
|
|
170
198
|
return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt));
|
171
199
|
}
|
172
200
|
|
173
|
-
static void *
|
201
|
+
static void *nogvl_stmt_execute(void *ptr) {
|
174
202
|
MYSQL_STMT *stmt = ptr;
|
175
203
|
|
176
204
|
if (mysql_stmt_execute(stmt)) {
|
@@ -196,7 +224,7 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
196
224
|
* the buffer is a Ruby string pointer and not our memory to manage.
|
197
225
|
*/
|
198
226
|
#define FREE_BINDS \
|
199
|
-
for (i = 0; i <
|
227
|
+
for (i = 0; i < bind_count; i++) { \
|
200
228
|
if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \
|
201
229
|
xfree(bind_buffers[i].buffer); \
|
202
230
|
} \
|
@@ -211,6 +239,8 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
|
211
239
|
{
|
212
240
|
unsigned LONG_LONG num;
|
213
241
|
size_t len;
|
242
|
+
// rb_absint_size was added in 2.1.0. See:
|
243
|
+
// https://github.com/ruby/ruby/commit/9fea875
|
214
244
|
#ifdef HAVE_RB_ABSINT_SIZE
|
215
245
|
int nlz_bits = 0;
|
216
246
|
len = rb_absint_size(bignum, &nlz_bits);
|
@@ -229,16 +259,15 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
|
229
259
|
#ifdef HAVE_RB_ABSINT_SIZE
|
230
260
|
nlz_bits == 0 &&
|
231
261
|
#endif
|
262
|
+
// rb_absint_singlebit_p was added in 2.1.0. See:
|
263
|
+
// https://github.com/ruby/ruby/commit/e5ff9d5
|
232
264
|
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
233
265
|
/* Optimized to avoid object allocation for Ruby 2.1+
|
234
266
|
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
235
267
|
*/
|
236
268
|
!rb_absint_singlebit_p(bignum)
|
237
|
-
#elif defined(HAVE_RB_BIG_CMP)
|
238
|
-
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
239
269
|
#else
|
240
|
-
|
241
|
-
rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
270
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
242
271
|
#endif
|
243
272
|
) {
|
244
273
|
goto overflow;
|
@@ -254,44 +283,45 @@ overflow:
|
|
254
283
|
*
|
255
284
|
* Executes the current prepared statement, returns +result+.
|
256
285
|
*/
|
257
|
-
static VALUE
|
286
|
+
static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
258
287
|
MYSQL_BIND *bind_buffers = NULL;
|
259
288
|
unsigned long *length_buffers = NULL;
|
260
289
|
unsigned long bind_count;
|
261
|
-
long i;
|
290
|
+
unsigned long i;
|
262
291
|
MYSQL_STMT *stmt;
|
263
292
|
MYSQL_RES *metadata;
|
293
|
+
VALUE opts;
|
264
294
|
VALUE current;
|
265
295
|
VALUE resultObj;
|
266
|
-
VALUE *params_enc;
|
296
|
+
VALUE *params_enc = NULL;
|
267
297
|
int is_streaming;
|
268
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
269
298
|
rb_encoding *conn_enc;
|
270
|
-
#endif
|
271
299
|
|
272
300
|
GET_STATEMENT(self);
|
273
301
|
GET_CLIENT(stmt_wrapper->client);
|
274
302
|
|
275
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
276
303
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
277
|
-
#endif
|
278
|
-
|
279
|
-
/* Scratch space for string encoding exports, allocate on the stack. */
|
280
|
-
params_enc = alloca(sizeof(VALUE) * argc);
|
281
304
|
|
282
305
|
stmt = stmt_wrapper->stmt;
|
283
|
-
|
284
306
|
bind_count = mysql_stmt_param_count(stmt);
|
285
|
-
|
286
|
-
|
307
|
+
|
308
|
+
// Get count of ordinary arguments, and extract hash opts/keyword arguments
|
309
|
+
// Use a local scope to avoid leaking the temporary count variable
|
310
|
+
{
|
311
|
+
int c = rb_scan_args(argc, argv, "*:", NULL, &opts);
|
312
|
+
if (c != (long)bind_count) {
|
313
|
+
rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c);
|
314
|
+
}
|
287
315
|
}
|
288
316
|
|
289
317
|
// setup any bind variables in the query
|
290
318
|
if (bind_count > 0) {
|
319
|
+
// Scratch space for string encoding exports, allocate on the stack
|
320
|
+
params_enc = alloca(sizeof(VALUE) * bind_count);
|
291
321
|
bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND));
|
292
322
|
length_buffers = xcalloc(bind_count, sizeof(unsigned long));
|
293
323
|
|
294
|
-
for (i = 0; i <
|
324
|
+
for (i = 0; i < bind_count; i++) {
|
295
325
|
bind_buffers[i].buffer = NULL;
|
296
326
|
params_enc[i] = Qnil;
|
297
327
|
|
@@ -319,12 +349,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
319
349
|
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
320
350
|
} else {
|
321
351
|
/* 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
352
|
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
324
|
-
params_enc[i] =
|
325
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
326
|
-
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
327
|
-
#endif
|
353
|
+
params_enc[i] = rb_str_export_to_enc(rb_big2str(argv[i], 10), conn_enc);
|
328
354
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
355
|
}
|
330
356
|
}
|
@@ -338,9 +364,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
338
364
|
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
339
365
|
|
340
366
|
params_enc[i] = argv[i];
|
341
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
342
367
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
343
|
-
#endif
|
344
368
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
345
369
|
break;
|
346
370
|
case T_TRUE:
|
@@ -405,9 +429,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
405
429
|
VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
|
406
430
|
|
407
431
|
params_enc[i] = rb_val_as_string;
|
408
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
409
432
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
410
|
-
#endif
|
411
433
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
412
434
|
}
|
413
435
|
break;
|
@@ -421,7 +443,40 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
421
443
|
}
|
422
444
|
}
|
423
445
|
|
424
|
-
|
446
|
+
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
447
|
+
current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
|
448
|
+
(void)RB_GC_GUARD(current);
|
449
|
+
Check_Type(current, T_HASH);
|
450
|
+
|
451
|
+
// Merge in hash opts/keyword arguments
|
452
|
+
if (!NIL_P(opts)) {
|
453
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
454
|
+
}
|
455
|
+
|
456
|
+
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
457
|
+
|
458
|
+
// From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no
|
459
|
+
// Ruby API calls are allowed so that GC is not invoked. If the connection is
|
460
|
+
// in results-streaming-mode for Statement A, and in the middle Statement B
|
461
|
+
// gets garbage collected, a message will be sent to the server notifying it
|
462
|
+
// to release Statement B, resulting in the following error:
|
463
|
+
// Commands out of sync; you can't run this command now
|
464
|
+
//
|
465
|
+
// In streaming mode, statement execute must return a cursor because we
|
466
|
+
// cannot prevent other Statement objects from being garbage collected
|
467
|
+
// between fetches of each row of the result set. The following error
|
468
|
+
// occurs if cursor mode is not set:
|
469
|
+
// Row retrieval was canceled by mysql_stmt_close
|
470
|
+
|
471
|
+
if (is_streaming) {
|
472
|
+
unsigned long type = CURSOR_TYPE_READ_ONLY;
|
473
|
+
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) {
|
474
|
+
FREE_BINDS;
|
475
|
+
rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY");
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
|
425
480
|
FREE_BINDS;
|
426
481
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
427
482
|
}
|
@@ -432,29 +487,26 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
432
487
|
if (metadata == NULL) {
|
433
488
|
if (mysql_stmt_errno(stmt) != 0) {
|
434
489
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
435
|
-
wrapper->
|
490
|
+
wrapper->active_fiber = Qnil;
|
436
491
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
437
492
|
}
|
438
493
|
// no data and no error, so query was not a SELECT
|
439
494
|
return Qnil;
|
440
495
|
}
|
441
496
|
|
442
|
-
current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
|
443
|
-
(void)RB_GC_GUARD(current);
|
444
|
-
Check_Type(current, T_HASH);
|
445
|
-
|
446
|
-
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
447
497
|
if (!is_streaming) {
|
448
|
-
//
|
498
|
+
// receive the whole result set from the server
|
449
499
|
if (mysql_stmt_store_result(stmt)) {
|
450
500
|
mysql_free_result(metadata);
|
451
501
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
452
502
|
}
|
453
|
-
wrapper->
|
503
|
+
wrapper->active_fiber = Qnil;
|
454
504
|
}
|
455
505
|
|
456
506
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
457
507
|
|
508
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
509
|
+
|
458
510
|
if (!is_streaming) {
|
459
511
|
// cache all result
|
460
512
|
rb_funcall(resultObj, intern_each, 0);
|
@@ -467,33 +519,29 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
467
519
|
*
|
468
520
|
* Returns a list of fields that will be returned by this statement.
|
469
521
|
*/
|
470
|
-
static VALUE
|
522
|
+
static VALUE rb_mysql_stmt_fields(VALUE self) {
|
471
523
|
MYSQL_FIELD *fields;
|
472
524
|
MYSQL_RES *metadata;
|
473
525
|
unsigned int field_count;
|
474
526
|
unsigned int i;
|
475
527
|
VALUE field_list;
|
476
528
|
MYSQL_STMT* stmt;
|
477
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
478
529
|
rb_encoding *default_internal_enc, *conn_enc;
|
479
|
-
#endif
|
480
530
|
GET_STATEMENT(self);
|
481
531
|
GET_CLIENT(stmt_wrapper->client);
|
482
532
|
stmt = stmt_wrapper->stmt;
|
483
533
|
|
484
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
485
534
|
default_internal_enc = rb_default_internal_encoding();
|
486
535
|
{
|
487
536
|
GET_CLIENT(stmt_wrapper->client);
|
488
537
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
489
538
|
}
|
490
|
-
#endif
|
491
539
|
|
492
540
|
metadata = mysql_stmt_result_metadata(stmt);
|
493
541
|
if (metadata == NULL) {
|
494
542
|
if (mysql_stmt_errno(stmt) != 0) {
|
495
543
|
// either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
|
496
|
-
wrapper->
|
544
|
+
wrapper->active_fiber = Qnil;
|
497
545
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
498
546
|
}
|
499
547
|
// no data and no error, so query was not a SELECT
|
@@ -508,12 +556,10 @@ static VALUE fields(VALUE self) {
|
|
508
556
|
VALUE rb_field;
|
509
557
|
|
510
558
|
rb_field = rb_str_new(fields[i].name, fields[i].name_length);
|
511
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
512
559
|
rb_enc_associate(rb_field, conn_enc);
|
513
560
|
if (default_internal_enc) {
|
514
561
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
515
562
|
}
|
516
|
-
#endif
|
517
563
|
|
518
564
|
rb_ary_store(field_list, (long)i, rb_field);
|
519
565
|
}
|
@@ -564,12 +610,23 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
|
|
564
610
|
}
|
565
611
|
|
566
612
|
void init_mysql2_statement() {
|
613
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
614
|
+
rb_global_variable(&cDate);
|
615
|
+
|
616
|
+
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
617
|
+
rb_global_variable(&cDateTime);
|
618
|
+
|
619
|
+
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
620
|
+
rb_global_variable(&cBigDecimal);
|
621
|
+
|
567
622
|
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
|
623
|
+
rb_undef_alloc_func(cMysql2Statement);
|
624
|
+
rb_global_variable(&cMysql2Statement);
|
568
625
|
|
569
|
-
rb_define_method(cMysql2Statement, "param_count",
|
570
|
-
rb_define_method(cMysql2Statement, "field_count",
|
571
|
-
rb_define_method(cMysql2Statement, "_execute",
|
572
|
-
rb_define_method(cMysql2Statement, "fields",
|
626
|
+
rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
|
627
|
+
rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
|
628
|
+
rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
|
629
|
+
rb_define_method(cMysql2Statement, "fields", rb_mysql_stmt_fields, 0);
|
573
630
|
rb_define_method(cMysql2Statement, "last_id", rb_mysql_stmt_last_id, 0);
|
574
631
|
rb_define_method(cMysql2Statement, "affected_rows", rb_mysql_stmt_affected_rows, 0);
|
575
632
|
rb_define_method(cMysql2Statement, "close", rb_mysql_stmt_close, 0);
|
@@ -589,7 +646,6 @@ void init_mysql2_statement() {
|
|
589
646
|
intern_year = rb_intern("year");
|
590
647
|
|
591
648
|
intern_to_s = rb_intern("to_s");
|
592
|
-
|
593
|
-
|
594
|
-
#endif
|
649
|
+
intern_merge_bang = rb_intern("merge!");
|
650
|
+
intern_query_options = rb_intern("@query_options");
|
595
651
|
}
|
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,22 +4,23 @@ 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
|
-
|
22
|
+
raise Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
23
|
+
|
23
24
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
24
25
|
@read_timeout = nil
|
25
26
|
@query_options = self.class.default_query_options.dup
|
@@ -31,8 +32,9 @@ module Mysql2
|
|
31
32
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
32
33
|
|
33
34
|
# TODO: stricter validation rather than silent massaging
|
34
|
-
[
|
35
|
+
%i[reconnect connect_timeout local_infile read_timeout write_timeout default_file default_group secure_auth init_command automatic_close enable_cleartext_plugin default_auth].each do |key|
|
35
36
|
next unless opts.key?(key)
|
37
|
+
|
36
38
|
case key
|
37
39
|
when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
|
38
40
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
@@ -46,25 +48,30 @@ module Mysql2
|
|
46
48
|
# force the encoding to utf8
|
47
49
|
self.charset_name = opts[:encoding] || 'utf8'
|
48
50
|
|
51
|
+
mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
52
|
+
if (mode == SSL_MODE_VERIFY_CA || mode == SSL_MODE_VERIFY_IDENTITY) && !opts[:sslca]
|
53
|
+
opts[:sslca] = find_default_ca_path
|
54
|
+
end
|
55
|
+
|
49
56
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
50
57
|
ssl_set(*ssl_options) if ssl_options.any? || opts.key?(:sslverify)
|
51
|
-
self.ssl_mode =
|
58
|
+
self.ssl_mode = mode if mode
|
52
59
|
|
53
|
-
case opts[:flags]
|
60
|
+
flags = case opts[:flags]
|
54
61
|
when Array
|
55
|
-
|
62
|
+
parse_flags_array(opts[:flags], @query_options[:connect_flags])
|
56
63
|
when String
|
57
|
-
|
64
|
+
parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
|
58
65
|
when Integer
|
59
|
-
|
66
|
+
@query_options[:connect_flags] | opts[:flags]
|
60
67
|
else
|
61
|
-
|
68
|
+
@query_options[:connect_flags]
|
62
69
|
end
|
63
70
|
|
64
71
|
# SSL verify is a connection flag rather than a mysql_ssl_set option
|
65
72
|
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
66
73
|
|
67
|
-
if [
|
74
|
+
if %i[user pass hostname dbname db sock].any? { |k| @query_options.key?(k) }
|
68
75
|
warn "============= WARNING FROM mysql2 ============="
|
69
76
|
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future."
|
70
77
|
warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options."
|
@@ -85,8 +92,9 @@ module Mysql2
|
|
85
92
|
port = port.to_i unless port.nil?
|
86
93
|
database = database.to_s unless database.nil?
|
87
94
|
socket = socket.to_s unless socket.nil?
|
95
|
+
conn_attrs = parse_connect_attrs(opts[:connect_attrs])
|
88
96
|
|
89
|
-
connect user, pass, host, port, database, socket, flags
|
97
|
+
connect user, pass, host, port, database, socket, flags, conn_attrs
|
90
98
|
end
|
91
99
|
|
92
100
|
def parse_ssl_mode(mode)
|
@@ -114,14 +122,32 @@ module Mysql2
|
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
125
|
+
# Find any default system CA paths to handle system roots
|
126
|
+
# by default if stricter validation is requested and no
|
127
|
+
# path is provide.
|
128
|
+
def find_default_ca_path
|
129
|
+
[
|
130
|
+
"/etc/ssl/certs/ca-certificates.crt",
|
131
|
+
"/etc/pki/tls/certs/ca-bundle.crt",
|
132
|
+
"/etc/ssl/ca-bundle.pem",
|
133
|
+
"/etc/ssl/cert.pem",
|
134
|
+
].find { |f| File.exist?(f) }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set default program_name in performance_schema.session_connect_attrs
|
138
|
+
# and performance_schema.session_account_connect_attrs
|
139
|
+
def parse_connect_attrs(conn_attrs)
|
140
|
+
return {} if Mysql2::Client::CONNECT_ATTRS.zero?
|
141
|
+
|
142
|
+
conn_attrs ||= {}
|
143
|
+
conn_attrs[:program_name] ||= $PROGRAM_NAME
|
144
|
+
conn_attrs.each_with_object({}) do |(key, value), hash|
|
145
|
+
hash[key.to_s] = value.to_s
|
122
146
|
end
|
123
|
-
|
124
|
-
|
147
|
+
end
|
148
|
+
|
149
|
+
def query(sql, options = {})
|
150
|
+
Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
|
125
151
|
_query(sql, @query_options.merge(options))
|
126
152
|
end
|
127
153
|
end
|
@@ -129,6 +155,7 @@ module Mysql2
|
|
129
155
|
def query_info
|
130
156
|
info = query_info_string
|
131
157
|
return {} unless info
|
158
|
+
|
132
159
|
info_hash = {}
|
133
160
|
info.split.each_slice(2) { |s| info_hash[s[0].downcase.delete(':').to_sym] = s[1].to_i }
|
134
161
|
info_hash
|
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
|