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.
@@ -1,9 +1,9 @@
1
1
  #include <mysql2_ext.h>
2
2
 
3
- VALUE cMysql2Statement;
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, intern_to_s;
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 * ptr) {
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 * ptr) {
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(args.sql, conn_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 param_count(VALUE self) {
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 field_count(VALUE self) {
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 *nogvl_execute(void *ptr) {
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 < argc; 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 execute(int argc, VALUE *argv, VALUE self) {
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
- if (argc != (long)bind_count) {
240
- rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, argc);
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 < argc; 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
- bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
269
- bind_buffers[i].buffer = xmalloc(sizeof(long long int));
270
- *(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[i]);
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
- params_enc[i] = argv[i];
280
- #ifdef HAVE_RUBY_ENCODING_H
281
- params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
282
- #endif
283
- set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
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
- t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
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(nogvl_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
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 fields(VALUE self) {
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 = mysql_stmt_result_metadata(stmt);
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
- cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
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
- rb_define_method(cMysql2Statement, "param_count", param_count, 0);
483
- rb_define_method(cMysql2Statement, "field_count", field_count, 0);
484
- rb_define_method(cMysql2Statement, "_execute", execute, -1);
485
- rb_define_method(cMysql2Statement, "fields", fields, 0);
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
  }
@@ -1,8 +1,6 @@
1
1
  #ifndef MYSQL2_STATEMENT_H
2
2
  #define MYSQL2_STATEMENT_H
3
3
 
4
- extern VALUE cMysql2Statement;
5
-
6
4
  typedef struct {
7
5
  VALUE client;
8
6
  MYSQL_STMT *stmt;
@@ -1,5 +1,6 @@
1
1
  /*
2
- * backwards compatibility for pre-1.9.3 C API
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
- :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,
15
- :cast => true,
16
- :default_file => nil,
17
- :default_group => nil,
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
- [:reconnect, :connect_timeout, :local_infile, :read_timeout, :write_timeout, :default_file, :default_group, :secure_auth, :init_command, :automatic_close].each do |key|
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
- flags = parse_flags_array(opts[:flags], @query_options[:connect_flags])
55
+ parse_flags_array(opts[:flags], @query_options[:connect_flags])
54
56
  when String
55
- flags = parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
57
+ parse_flags_array(opts[:flags].split(' '), @query_options[:connect_flags])
56
58
  when Integer
57
- flags = @query_options[:connect_flags] | opts[:flags]
59
+ @query_options[:connect_flags] | opts[:flags]
58
60
  else
59
- flags = @query_options[:connect_flags]
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] && ssl_options.any?
65
+ flags |= SSL_VERIFY_SERVER_CERT if opts[:sslverify]
64
66
 
65
- if [:user, :pass, :hostname, :dbname, :db, :sock].any? { |k| @query_options.key?(k) }
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 deprecated at some point in the future."
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
- if Thread.respond_to?(:handle_interrupt)
105
- def query(sql, options = {})
106
- Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do
107
- _query(sql, @query_options.merge(options))
108
- end
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
- else
111
- def query(sql, options = {})
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(:async => true))
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