mysql2 0.4.10 → 0.5.4
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 +98 -38
- data/ext/mysql2/client.c +223 -76
- data/ext/mysql2/client.h +1 -39
- data/ext/mysql2/extconf.rb +46 -26
- data/ext/mysql2/mysql2_ext.c +8 -2
- data/ext/mysql2/mysql2_ext.h +8 -4
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
- data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
- data/ext/mysql2/result.c +242 -86
- data/ext/mysql2/result.h +3 -3
- data/ext/mysql2/statement.c +90 -73
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +51 -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 +18 -15
- data/support/3A79BD29.asc +49 -0
- data/support/5072E1F5.asc +5 -5
- data/support/mysql_enc_to_ruby.rb +8 -3
- data/support/ruby_enc_to_mysql.rb +7 -5
- metadata +14 -58
- 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,12 +1,10 @@
|
|
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
|
-
static ID id_cmp;
|
9
|
-
#endif
|
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;
|
10
8
|
|
11
9
|
#define GET_STATEMENT(self) \
|
12
10
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -21,7 +19,7 @@ static void rb_mysql_stmt_mark(void * ptr) {
|
|
21
19
|
rb_gc_mark(stmt_wrapper->client);
|
22
20
|
}
|
23
21
|
|
24
|
-
static void *nogvl_stmt_close(void *
|
22
|
+
static void *nogvl_stmt_close(void *ptr) {
|
25
23
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
26
24
|
if (stmt_wrapper->stmt) {
|
27
25
|
mysql_stmt_close(stmt_wrapper->stmt);
|
@@ -30,7 +28,7 @@ static void *nogvl_stmt_close(void * ptr) {
|
|
30
28
|
return NULL;
|
31
29
|
}
|
32
30
|
|
33
|
-
static void rb_mysql_stmt_free(void *
|
31
|
+
static void rb_mysql_stmt_free(void *ptr) {
|
34
32
|
mysql_stmt_wrapper *stmt_wrapper = ptr;
|
35
33
|
decr_mysql2_stmt(stmt_wrapper);
|
36
34
|
}
|
@@ -48,9 +46,8 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
48
46
|
VALUE e;
|
49
47
|
GET_CLIENT(stmt_wrapper->client);
|
50
48
|
VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
|
51
|
-
VALUE rb_sql_state =
|
49
|
+
VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
|
52
50
|
|
53
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
54
51
|
rb_encoding *conn_enc;
|
55
52
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
56
53
|
|
@@ -62,7 +59,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
|
|
62
59
|
rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
|
63
60
|
rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
|
64
61
|
}
|
65
|
-
#endif
|
66
62
|
|
67
63
|
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
68
64
|
rb_error_msg,
|
@@ -96,9 +92,7 @@ static void *nogvl_prepare_statement(void *ptr) {
|
|
96
92
|
VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
97
93
|
mysql_stmt_wrapper *stmt_wrapper;
|
98
94
|
VALUE rb_stmt;
|
99
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
100
95
|
rb_encoding *conn_enc;
|
101
|
-
#endif
|
102
96
|
|
103
97
|
Check_Type(sql, T_STRING);
|
104
98
|
|
@@ -114,9 +108,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
114
108
|
{
|
115
109
|
GET_CLIENT(rb_client);
|
116
110
|
stmt_wrapper->stmt = mysql_stmt_init(wrapper->client);
|
117
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
118
111
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
119
|
-
#endif
|
120
112
|
}
|
121
113
|
if (stmt_wrapper->stmt == NULL) {
|
122
114
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: out of memory");
|
@@ -124,7 +116,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
124
116
|
|
125
117
|
// set STMT_ATTR_UPDATE_MAX_LENGTH attr
|
126
118
|
{
|
127
|
-
|
119
|
+
my_bool truth = 1;
|
128
120
|
if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
|
129
121
|
rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
|
130
122
|
}
|
@@ -134,11 +126,8 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
134
126
|
{
|
135
127
|
struct nogvl_prepare_statement_args args;
|
136
128
|
args.stmt = stmt_wrapper->stmt;
|
137
|
-
args.sql = sql;
|
138
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
139
129
|
// ensure the string is in the encoding the connection is expecting
|
140
|
-
args.sql = rb_str_export_to_enc(
|
141
|
-
#endif
|
130
|
+
args.sql = rb_str_export_to_enc(sql, conn_enc);
|
142
131
|
args.sql_ptr = RSTRING_PTR(sql);
|
143
132
|
args.sql_len = RSTRING_LEN(sql);
|
144
133
|
|
@@ -154,7 +143,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
|
|
154
143
|
*
|
155
144
|
* Returns the number of parameters the prepared statement expects.
|
156
145
|
*/
|
157
|
-
static VALUE
|
146
|
+
static VALUE rb_mysql_stmt_param_count(VALUE self) {
|
158
147
|
GET_STATEMENT(self);
|
159
148
|
|
160
149
|
return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt));
|
@@ -164,13 +153,13 @@ static VALUE param_count(VALUE self) {
|
|
164
153
|
*
|
165
154
|
* Returns the number of fields the prepared statement returns.
|
166
155
|
*/
|
167
|
-
static VALUE
|
156
|
+
static VALUE rb_mysql_stmt_field_count(VALUE self) {
|
168
157
|
GET_STATEMENT(self);
|
169
158
|
|
170
159
|
return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt));
|
171
160
|
}
|
172
161
|
|
173
|
-
static void *
|
162
|
+
static void *nogvl_stmt_execute(void *ptr) {
|
174
163
|
MYSQL_STMT *stmt = ptr;
|
175
164
|
|
176
165
|
if (mysql_stmt_execute(stmt)) {
|
@@ -196,7 +185,7 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
196
185
|
* the buffer is a Ruby string pointer and not our memory to manage.
|
197
186
|
*/
|
198
187
|
#define FREE_BINDS \
|
199
|
-
for (i = 0; i <
|
188
|
+
for (i = 0; i < bind_count; i++) { \
|
200
189
|
if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \
|
201
190
|
xfree(bind_buffers[i].buffer); \
|
202
191
|
} \
|
@@ -211,6 +200,8 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
|
211
200
|
{
|
212
201
|
unsigned LONG_LONG num;
|
213
202
|
size_t len;
|
203
|
+
// rb_absint_size was added in 2.1.0. See:
|
204
|
+
// https://github.com/ruby/ruby/commit/9fea875
|
214
205
|
#ifdef HAVE_RB_ABSINT_SIZE
|
215
206
|
int nlz_bits = 0;
|
216
207
|
len = rb_absint_size(bignum, &nlz_bits);
|
@@ -229,16 +220,15 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
|
229
220
|
#ifdef HAVE_RB_ABSINT_SIZE
|
230
221
|
nlz_bits == 0 &&
|
231
222
|
#endif
|
223
|
+
// rb_absint_singlebit_p was added in 2.1.0. See:
|
224
|
+
// https://github.com/ruby/ruby/commit/e5ff9d5
|
232
225
|
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
233
226
|
/* Optimized to avoid object allocation for Ruby 2.1+
|
234
227
|
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
235
228
|
*/
|
236
229
|
!rb_absint_singlebit_p(bignum)
|
237
|
-
#elif defined(HAVE_RB_BIG_CMP)
|
238
|
-
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
239
230
|
#else
|
240
|
-
|
241
|
-
rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
231
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
242
232
|
#endif
|
243
233
|
) {
|
244
234
|
goto overflow;
|
@@ -254,44 +244,45 @@ overflow:
|
|
254
244
|
*
|
255
245
|
* Executes the current prepared statement, returns +result+.
|
256
246
|
*/
|
257
|
-
static VALUE
|
247
|
+
static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
|
258
248
|
MYSQL_BIND *bind_buffers = NULL;
|
259
249
|
unsigned long *length_buffers = NULL;
|
260
250
|
unsigned long bind_count;
|
261
|
-
long i;
|
251
|
+
unsigned long i;
|
262
252
|
MYSQL_STMT *stmt;
|
263
253
|
MYSQL_RES *metadata;
|
254
|
+
VALUE opts;
|
264
255
|
VALUE current;
|
265
256
|
VALUE resultObj;
|
266
|
-
VALUE *params_enc;
|
257
|
+
VALUE *params_enc = NULL;
|
267
258
|
int is_streaming;
|
268
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
269
259
|
rb_encoding *conn_enc;
|
270
|
-
#endif
|
271
260
|
|
272
261
|
GET_STATEMENT(self);
|
273
262
|
GET_CLIENT(stmt_wrapper->client);
|
274
263
|
|
275
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
276
264
|
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
265
|
|
282
266
|
stmt = stmt_wrapper->stmt;
|
283
|
-
|
284
267
|
bind_count = mysql_stmt_param_count(stmt);
|
285
|
-
|
286
|
-
|
268
|
+
|
269
|
+
// Get count of ordinary arguments, and extract hash opts/keyword arguments
|
270
|
+
// Use a local scope to avoid leaking the temporary count variable
|
271
|
+
{
|
272
|
+
int c = rb_scan_args(argc, argv, "*:", NULL, &opts);
|
273
|
+
if (c != (long)bind_count) {
|
274
|
+
rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c);
|
275
|
+
}
|
287
276
|
}
|
288
277
|
|
289
278
|
// setup any bind variables in the query
|
290
279
|
if (bind_count > 0) {
|
280
|
+
// Scratch space for string encoding exports, allocate on the stack
|
281
|
+
params_enc = alloca(sizeof(VALUE) * bind_count);
|
291
282
|
bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND));
|
292
283
|
length_buffers = xcalloc(bind_count, sizeof(unsigned long));
|
293
284
|
|
294
|
-
for (i = 0; i <
|
285
|
+
for (i = 0; i < bind_count; i++) {
|
295
286
|
bind_buffers[i].buffer = NULL;
|
296
287
|
params_enc[i] = Qnil;
|
297
288
|
|
@@ -319,12 +310,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
319
310
|
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
320
311
|
} else {
|
321
312
|
/* 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
313
|
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
|
314
|
+
params_enc[i] = rb_str_export_to_enc(rb_big2str(argv[i], 10), conn_enc);
|
328
315
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
316
|
}
|
330
317
|
}
|
@@ -338,9 +325,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
338
325
|
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
339
326
|
|
340
327
|
params_enc[i] = argv[i];
|
341
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
342
328
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
343
|
-
#endif
|
344
329
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
345
330
|
break;
|
346
331
|
case T_TRUE:
|
@@ -405,9 +390,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
405
390
|
VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
|
406
391
|
|
407
392
|
params_enc[i] = rb_val_as_string;
|
408
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
409
393
|
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
410
|
-
#endif
|
411
394
|
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
412
395
|
}
|
413
396
|
break;
|
@@ -421,7 +404,40 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
421
404
|
}
|
422
405
|
}
|
423
406
|
|
424
|
-
|
407
|
+
// Duplicate the options hash, merge! extra opts, put the copy into the Result object
|
408
|
+
current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
|
409
|
+
(void)RB_GC_GUARD(current);
|
410
|
+
Check_Type(current, T_HASH);
|
411
|
+
|
412
|
+
// Merge in hash opts/keyword arguments
|
413
|
+
if (!NIL_P(opts)) {
|
414
|
+
rb_funcall(current, intern_merge_bang, 1, opts);
|
415
|
+
}
|
416
|
+
|
417
|
+
is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
|
418
|
+
|
419
|
+
// From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no
|
420
|
+
// Ruby API calls are allowed so that GC is not invoked. If the connection is
|
421
|
+
// in results-streaming-mode for Statement A, and in the middle Statement B
|
422
|
+
// gets garbage collected, a message will be sent to the server notifying it
|
423
|
+
// to release Statement B, resulting in the following error:
|
424
|
+
// Commands out of sync; you can't run this command now
|
425
|
+
//
|
426
|
+
// In streaming mode, statement execute must return a cursor because we
|
427
|
+
// cannot prevent other Statement objects from being garbage collected
|
428
|
+
// between fetches of each row of the result set. The following error
|
429
|
+
// occurs if cursor mode is not set:
|
430
|
+
// Row retrieval was canceled by mysql_stmt_close
|
431
|
+
|
432
|
+
if (is_streaming) {
|
433
|
+
unsigned long type = CURSOR_TYPE_READ_ONLY;
|
434
|
+
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) {
|
435
|
+
FREE_BINDS;
|
436
|
+
rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY");
|
437
|
+
}
|
438
|
+
}
|
439
|
+
|
440
|
+
if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
|
425
441
|
FREE_BINDS;
|
426
442
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
427
443
|
}
|
@@ -439,13 +455,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
439
455
|
return Qnil;
|
440
456
|
}
|
441
457
|
|
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
458
|
if (!is_streaming) {
|
448
|
-
//
|
459
|
+
// receive the whole result set from the server
|
449
460
|
if (mysql_stmt_store_result(stmt)) {
|
450
461
|
mysql_free_result(metadata);
|
451
462
|
rb_raise_mysql2_stmt_error(stmt_wrapper);
|
@@ -455,6 +466,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
455
466
|
|
456
467
|
resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
|
457
468
|
|
469
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
470
|
+
|
458
471
|
if (!is_streaming) {
|
459
472
|
// cache all result
|
460
473
|
rb_funcall(resultObj, intern_each, 0);
|
@@ -467,27 +480,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
467
480
|
*
|
468
481
|
* Returns a list of fields that will be returned by this statement.
|
469
482
|
*/
|
470
|
-
static VALUE
|
483
|
+
static VALUE rb_mysql_stmt_fields(VALUE self) {
|
471
484
|
MYSQL_FIELD *fields;
|
472
485
|
MYSQL_RES *metadata;
|
473
486
|
unsigned int field_count;
|
474
487
|
unsigned int i;
|
475
488
|
VALUE field_list;
|
476
489
|
MYSQL_STMT* stmt;
|
477
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
478
490
|
rb_encoding *default_internal_enc, *conn_enc;
|
479
|
-
#endif
|
480
491
|
GET_STATEMENT(self);
|
481
492
|
GET_CLIENT(stmt_wrapper->client);
|
482
493
|
stmt = stmt_wrapper->stmt;
|
483
494
|
|
484
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
485
495
|
default_internal_enc = rb_default_internal_encoding();
|
486
496
|
{
|
487
497
|
GET_CLIENT(stmt_wrapper->client);
|
488
498
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
489
499
|
}
|
490
|
-
#endif
|
491
500
|
|
492
501
|
metadata = mysql_stmt_result_metadata(stmt);
|
493
502
|
if (metadata == NULL) {
|
@@ -508,12 +517,10 @@ static VALUE fields(VALUE self) {
|
|
508
517
|
VALUE rb_field;
|
509
518
|
|
510
519
|
rb_field = rb_str_new(fields[i].name, fields[i].name_length);
|
511
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
512
520
|
rb_enc_associate(rb_field, conn_enc);
|
513
521
|
if (default_internal_enc) {
|
514
522
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
515
523
|
}
|
516
|
-
#endif
|
517
524
|
|
518
525
|
rb_ary_store(field_list, (long)i, rb_field);
|
519
526
|
}
|
@@ -564,12 +571,23 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
|
|
564
571
|
}
|
565
572
|
|
566
573
|
void init_mysql2_statement() {
|
574
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
575
|
+
rb_global_variable(&cDate);
|
576
|
+
|
577
|
+
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
578
|
+
rb_global_variable(&cDateTime);
|
579
|
+
|
580
|
+
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
581
|
+
rb_global_variable(&cBigDecimal);
|
582
|
+
|
567
583
|
cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
|
584
|
+
rb_undef_alloc_func(cMysql2Statement);
|
585
|
+
rb_global_variable(&cMysql2Statement);
|
568
586
|
|
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",
|
587
|
+
rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
|
588
|
+
rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
|
589
|
+
rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
|
590
|
+
rb_define_method(cMysql2Statement, "fields", rb_mysql_stmt_fields, 0);
|
573
591
|
rb_define_method(cMysql2Statement, "last_id", rb_mysql_stmt_last_id, 0);
|
574
592
|
rb_define_method(cMysql2Statement, "affected_rows", rb_mysql_stmt_affected_rows, 0);
|
575
593
|
rb_define_method(cMysql2Statement, "close", rb_mysql_stmt_close, 0);
|
@@ -589,7 +607,6 @@ void init_mysql2_statement() {
|
|
589
607
|
intern_year = rb_intern("year");
|
590
608
|
|
591
609
|
intern_to_s = rb_intern("to_s");
|
592
|
-
|
593
|
-
|
594
|
-
#endif
|
610
|
+
intern_merge_bang = rb_intern("merge!");
|
611
|
+
intern_query_options = rb_intern("@query_options");
|
595
612
|
}
|
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,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
|
-
|
22
|
+
raise Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
23
23
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
24
24
|
@read_timeout = nil
|
25
25
|
@query_options = self.class.default_query_options.dup
|
@@ -31,7 +31,7 @@ module Mysql2
|
|
31
31
|
opts[:connect_timeout] = 120 unless opts.key?(:connect_timeout)
|
32
32
|
|
33
33
|
# TODO: stricter validation rather than silent massaging
|
34
|
-
[
|
34
|
+
%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
35
|
next unless opts.key?(key)
|
36
36
|
case key
|
37
37
|
when :reconnect, :local_infile, :secure_auth, :automatic_close, :enable_cleartext_plugin
|
@@ -46,25 +46,30 @@ module Mysql2
|
|
46
46
|
# force the encoding to utf8
|
47
47
|
self.charset_name = opts[:encoding] || 'utf8'
|
48
48
|
|
49
|
+
mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
50
|
+
if (mode == SSL_MODE_VERIFY_CA || mode == SSL_MODE_VERIFY_IDENTITY) && !opts[:sslca]
|
51
|
+
opts[:sslca] = find_default_ca_path
|
52
|
+
end
|
53
|
+
|
49
54
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
50
55
|
ssl_set(*ssl_options) if ssl_options.any? || opts.key?(:sslverify)
|
51
|
-
self.ssl_mode =
|
56
|
+
self.ssl_mode = mode if mode
|
52
57
|
|
53
|
-
case opts[:flags]
|
58
|
+
flags = case opts[:flags]
|
54
59
|
when Array
|
55
|
-
|
60
|
+
parse_flags_array(opts[:flags], @query_options[:connect_flags])
|
56
61
|
when String
|
57
|
-
|
62
|
+
parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
|
58
63
|
when Integer
|
59
|
-
|
64
|
+
@query_options[:connect_flags] | opts[:flags]
|
60
65
|
else
|
61
|
-
|
66
|
+
@query_options[:connect_flags]
|
62
67
|
end
|
63
68
|
|
64
69
|
# SSL verify is a connection flag rather than a mysql_ssl_set option
|
65
70
|
flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
|
66
71
|
|
67
|
-
if [
|
72
|
+
if %i[user pass hostname dbname db sock].any? { |k| @query_options.key?(k) }
|
68
73
|
warn "============= WARNING FROM mysql2 ============="
|
69
74
|
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock are deprecated and will be removed at some point in the future."
|
70
75
|
warn "Instead, please use :username, :password, :host, :port, :database, :socket, :flags for the options."
|
@@ -85,8 +90,9 @@ module Mysql2
|
|
85
90
|
port = port.to_i unless port.nil?
|
86
91
|
database = database.to_s unless database.nil?
|
87
92
|
socket = socket.to_s unless socket.nil?
|
93
|
+
conn_attrs = parse_connect_attrs(opts[:connect_attrs])
|
88
94
|
|
89
|
-
connect user, pass, host, port, database, socket, flags
|
95
|
+
connect user, pass, host, port, database, socket, flags, conn_attrs
|
90
96
|
end
|
91
97
|
|
92
98
|
def parse_ssl_mode(mode)
|
@@ -114,14 +120,31 @@ module Mysql2
|
|
114
120
|
end
|
115
121
|
end
|
116
122
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
123
|
+
# Find any default system CA paths to handle system roots
|
124
|
+
# by default if stricter validation is requested and no
|
125
|
+
# path is provide.
|
126
|
+
def find_default_ca_path
|
127
|
+
[
|
128
|
+
"/etc/ssl/certs/ca-certificates.crt",
|
129
|
+
"/etc/pki/tls/certs/ca-bundle.crt",
|
130
|
+
"/etc/ssl/ca-bundle.pem",
|
131
|
+
"/etc/ssl/cert.pem",
|
132
|
+
].find { |f| File.exist?(f) }
|
133
|
+
end
|
134
|
+
|
135
|
+
# Set default program_name in performance_schema.session_connect_attrs
|
136
|
+
# and performance_schema.session_account_connect_attrs
|
137
|
+
def parse_connect_attrs(conn_attrs)
|
138
|
+
return {} if Mysql2::Client::CONNECT_ATTRS.zero?
|
139
|
+
conn_attrs ||= {}
|
140
|
+
conn_attrs[:program_name] ||= $PROGRAM_NAME
|
141
|
+
conn_attrs.each_with_object({}) do |(key, value), hash|
|
142
|
+
hash[key.to_s] = value.to_s
|
122
143
|
end
|
123
|
-
|
124
|
-
|
144
|
+
end
|
145
|
+
|
146
|
+
def query(sql, options = {})
|
147
|
+
Thread.handle_interrupt(::Mysql2::Util::TIMEOUT_ERROR_NEVER) do
|
125
148
|
_query(sql, @query_options.merge(options))
|
126
149
|
end
|
127
150
|
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
|
data/lib/mysql2/error.rb
CHANGED
@@ -1,32 +1,66 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
module Mysql2
|
4
2
|
class Error < StandardError
|
5
3
|
ENCODE_OPTS = {
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
4
|
+
undef: :replace,
|
5
|
+
invalid: :replace,
|
6
|
+
replace: '?'.freeze,
|
7
|
+
}.freeze
|
8
|
+
|
9
|
+
ConnectionError = Class.new(Error)
|
10
|
+
TimeoutError = Class.new(Error)
|
11
|
+
|
12
|
+
CODES = {
|
13
|
+
1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
|
14
|
+
|
15
|
+
1044 => ConnectionError, # ER_DBACCESS_DENIED_ERROR
|
16
|
+
1045 => ConnectionError, # ER_ACCESS_DENIED_ERROR
|
17
|
+
1152 => ConnectionError, # ER_ABORTING_CONNECTION
|
18
|
+
1153 => ConnectionError, # ER_NET_PACKET_TOO_LARGE
|
19
|
+
1154 => ConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
|
20
|
+
1155 => ConnectionError, # ER_NET_FCNTL_ERROR
|
21
|
+
1156 => ConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
|
22
|
+
1157 => ConnectionError, # ER_NET_UNCOMPRESS_ERROR
|
23
|
+
1158 => ConnectionError, # ER_NET_READ_ERROR
|
24
|
+
1159 => ConnectionError, # ER_NET_READ_INTERRUPTED
|
25
|
+
1160 => ConnectionError, # ER_NET_ERROR_ON_WRITE
|
26
|
+
1161 => ConnectionError, # ER_NET_WRITE_INTERRUPTED
|
27
|
+
1927 => ConnectionError, # ER_CONNECTION_KILLED
|
28
|
+
|
29
|
+
2001 => ConnectionError, # CR_SOCKET_CREATE_ERROR
|
30
|
+
2002 => ConnectionError, # CR_CONNECTION_ERROR
|
31
|
+
2003 => ConnectionError, # CR_CONN_HOST_ERROR
|
32
|
+
2004 => ConnectionError, # CR_IPSOCK_ERROR
|
33
|
+
2005 => ConnectionError, # CR_UNKNOWN_HOST
|
34
|
+
2006 => ConnectionError, # CR_SERVER_GONE_ERROR
|
35
|
+
2007 => ConnectionError, # CR_VERSION_ERROR
|
36
|
+
2009 => ConnectionError, # CR_WRONG_HOST_INFO
|
37
|
+
2012 => ConnectionError, # CR_SERVER_HANDSHAKE_ERR
|
38
|
+
2013 => ConnectionError, # CR_SERVER_LOST
|
39
|
+
2020 => ConnectionError, # CR_NET_PACKET_TOO_LARGE
|
40
|
+
2026 => ConnectionError, # CR_SSL_CONNECTION_ERROR
|
41
|
+
2027 => ConnectionError, # CR_MALFORMED_PACKET
|
42
|
+
2047 => ConnectionError, # CR_CONN_UNKNOW_PROTOCOL
|
43
|
+
2048 => ConnectionError, # CR_INVALID_CONN_HANDLE
|
44
|
+
2049 => ConnectionError, # CR_UNUSED_1
|
9
45
|
}.freeze
|
10
46
|
|
11
47
|
attr_reader :error_number, :sql_state
|
12
48
|
|
13
49
|
# Mysql gem compatibility
|
14
|
-
|
15
|
-
|
50
|
+
alias errno error_number
|
51
|
+
alias error message
|
16
52
|
|
17
|
-
def initialize(msg)
|
18
|
-
@server_version
|
53
|
+
def initialize(msg, server_version = nil, error_number = nil, sql_state = nil)
|
54
|
+
@server_version = server_version
|
55
|
+
@error_number = error_number
|
56
|
+
@sql_state = sql_state ? sql_state.encode(**ENCODE_OPTS) : nil
|
19
57
|
|
20
58
|
super(clean_message(msg))
|
21
59
|
end
|
22
60
|
|
23
61
|
def self.new_with_args(msg, server_version, error_number, sql_state)
|
24
|
-
|
25
|
-
|
26
|
-
err.instance_variable_set('@error_number', error_number)
|
27
|
-
err.instance_variable_set('@sql_state', sql_state.respond_to?(:encode) ? sql_state.encode(ENCODE_OPTS) : sql_state)
|
28
|
-
err.send(:initialize, msg)
|
29
|
-
err
|
62
|
+
error_class = CODES.fetch(error_number, self)
|
63
|
+
error_class.new(msg, server_version, error_number, sql_state)
|
30
64
|
end
|
31
65
|
|
32
66
|
private
|
@@ -55,16 +89,12 @@ module Mysql2
|
|
55
89
|
# encoding, we'll assume UTF-8 and clean the string of anything that's not a
|
56
90
|
# valid UTF-8 character.
|
57
91
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# Returns a valid UTF-8 string in Ruby 1.9+, the original string on Ruby 1.8
|
92
|
+
# Returns a valid UTF-8 string.
|
61
93
|
def clean_message(message)
|
62
|
-
return message unless message.respond_to?(:encode)
|
63
|
-
|
64
94
|
if @server_version && @server_version > 50500
|
65
|
-
message.encode(ENCODE_OPTS)
|
95
|
+
message.encode(**ENCODE_OPTS)
|
66
96
|
else
|
67
|
-
message.encode(Encoding::UTF_8, ENCODE_OPTS)
|
97
|
+
message.encode(Encoding::UTF_8, **ENCODE_OPTS)
|
68
98
|
end
|
69
99
|
end
|
70
100
|
end
|