mysql2 0.4.4 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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