mysql2 0.4.2 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
data/ext/mysql2/result.c CHANGED
@@ -104,6 +104,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
104
104
  wrapper->stmt_wrapper->stmt->bind_result_done = 0;
105
105
  }
106
106
 
107
+ if (wrapper->statement != Qnil) {
108
+ decr_mysql2_stmt(wrapper->stmt_wrapper);
109
+ }
110
+
107
111
  if (wrapper->result_buffers) {
108
112
  unsigned int i;
109
113
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -136,13 +140,15 @@ static void rb_mysql_result_free(void *ptr) {
136
140
  decr_mysql2_client(wrapper->client_wrapper);
137
141
  }
138
142
 
139
- if (wrapper->statement != Qnil) {
140
- decr_mysql2_stmt(wrapper->stmt_wrapper);
141
- }
142
-
143
143
  xfree(wrapper);
144
144
  }
145
145
 
146
+ static VALUE rb_mysql_result_free_(VALUE self) {
147
+ GET_RESULT(self);
148
+ rb_mysql_result_free_result(wrapper);
149
+ return Qnil;
150
+ }
151
+
146
152
  /*
147
153
  * for small results, this won't hit the network, but there's no
148
154
  * reliable way for us to tell this so we'll always release the GVL
@@ -256,8 +262,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
256
262
  if (wrapper->result_buffers != NULL) return;
257
263
 
258
264
  wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
259
- wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
260
- wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
265
+ wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
266
+ wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
261
267
  wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
262
268
 
263
269
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -340,15 +346,15 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
340
346
  conn_enc = rb_to_encoding(wrapper->encoding);
341
347
  #endif
342
348
 
349
+ if (wrapper->fields == Qnil) {
350
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
351
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
352
+ }
343
353
  if (args->asArray) {
344
354
  rowVal = rb_ary_new2(wrapper->numberOfFields);
345
355
  } else {
346
356
  rowVal = rb_hash_new();
347
357
  }
348
- if (wrapper->fields == Qnil) {
349
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
350
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
351
- }
352
358
 
353
359
  if (wrapper->result_buffers == NULL) {
354
360
  rb_mysql_result_alloc_result_buffers(self, fields);
@@ -399,6 +405,13 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
399
405
  val = INT2NUM(*((signed char*)result_buffer->buffer));
400
406
  }
401
407
  break;
408
+ case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
409
+ if (args->castBool && fields[i].length == 1) {
410
+ val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
411
+ }else{
412
+ val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
413
+ }
414
+ break;
402
415
  case MYSQL_TYPE_SHORT: // short int
403
416
  if (result_buffer->is_unsigned) {
404
417
  val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
@@ -488,7 +501,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
488
501
  case MYSQL_TYPE_BLOB: // char[]
489
502
  case MYSQL_TYPE_MEDIUM_BLOB: // char[]
490
503
  case MYSQL_TYPE_LONG_BLOB: // char[]
491
- case MYSQL_TYPE_BIT: // char[]
492
504
  case MYSQL_TYPE_SET: // char[]
493
505
  case MYSQL_TYPE_ENUM: // char[]
494
506
  case MYSQL_TYPE_GEOMETRY: // char[]
@@ -511,7 +523,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
511
523
  return rowVal;
512
524
  }
513
525
 
514
-
515
526
  static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
516
527
  {
517
528
  VALUE rowVal;
@@ -536,16 +547,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
536
547
  return Qnil;
537
548
  }
538
549
 
550
+ if (wrapper->fields == Qnil) {
551
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
552
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
553
+ }
539
554
  if (args->asArray) {
540
555
  rowVal = rb_ary_new2(wrapper->numberOfFields);
541
556
  } else {
542
557
  rowVal = rb_hash_new();
543
558
  }
544
559
  fieldLengths = mysql_fetch_lengths(wrapper->result);
545
- if (wrapper->fields == Qnil) {
546
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
547
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
548
- }
549
560
 
550
561
  for (i = 0; i < wrapper->numberOfFields; i++) {
551
562
  VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
@@ -829,7 +840,9 @@ static VALUE rb_mysql_result_each_(VALUE self,
829
840
 
830
841
  if (row == Qnil) {
831
842
  /* we don't need the mysql C dataset around anymore, peace it */
832
- rb_mysql_result_free_result(wrapper);
843
+ if (args->cacheRows) {
844
+ rb_mysql_result_free_result(wrapper);
845
+ }
833
846
  return Qnil;
834
847
  }
835
848
 
@@ -837,7 +850,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
837
850
  rb_yield(row);
838
851
  }
839
852
  }
840
- if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
853
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
841
854
  /* we don't need the mysql C dataset around anymore, peace it */
842
855
  rb_mysql_result_free_result(wrapper);
843
856
  }
@@ -881,6 +894,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
881
894
 
882
895
  if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
883
896
  rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
897
+ cacheRows = 1;
884
898
  }
885
899
 
886
900
  if (wrapper->stmt_wrapper && !cast) {
@@ -908,12 +922,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
908
922
  app_timezone = Qnil;
909
923
  }
910
924
 
911
- if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
925
+ if (wrapper->rows == Qnil && !wrapper->is_streaming) {
912
926
  wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
913
- if (wrapper->numberOfRows == 0) {
914
- wrapper->rows = rb_ary_new();
915
- return wrapper->rows;
927
+ wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
928
+ } else if (wrapper->rows && !cacheRows) {
929
+ if (wrapper->resultFreed) {
930
+ rb_raise(cMysql2Error, "Result set has already been freed");
916
931
  }
932
+ mysql_data_seek(wrapper->result, 0);
933
+ wrapper->lastRowProcessed = 0;
917
934
  wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
918
935
  }
919
936
 
@@ -1007,6 +1024,7 @@ void init_mysql2_result() {
1007
1024
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1008
1025
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
1009
1026
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1027
+ rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
1010
1028
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
1011
1029
  rb_define_alias(cMysql2Result, "size", "count");
1012
1030
 
data/ext/mysql2/result.h CHANGED
@@ -1,5 +1,6 @@
1
1
  #ifndef MYSQL2_RESULT_H
2
2
  #define MYSQL2_RESULT_H
3
+ #include <stdbool.h>
3
4
 
4
5
  void init_mysql2_result(void);
5
6
  VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement);
@@ -21,8 +22,8 @@ typedef struct {
21
22
  mysql_client_wrapper *client_wrapper;
22
23
  /* statement result bind buffers */
23
24
  MYSQL_BIND *result_buffers;
24
- my_bool *is_null;
25
- my_bool *error;
25
+ bool *is_null;
26
+ bool *error;
26
27
  unsigned long *length;
27
28
  } mysql2_result_wrapper;
28
29
 
@@ -2,8 +2,11 @@
2
2
 
3
3
  VALUE cMysql2Statement;
4
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;
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
+ #ifndef HAVE_RB_BIG_CMP
8
+ static ID id_cmp;
9
+ #endif
7
10
 
8
11
  #define GET_STATEMENT(self) \
9
12
  mysql_stmt_wrapper *stmt_wrapper; \
@@ -11,7 +14,6 @@ static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, inter
11
14
  if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
12
15
  if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
13
16
 
14
-
15
17
  static void rb_mysql_stmt_mark(void * ptr) {
16
18
  mysql_stmt_wrapper *stmt_wrapper = ptr;
17
19
  if (!stmt_wrapper) return;
@@ -42,7 +44,6 @@ void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
42
44
  }
43
45
  }
44
46
 
45
-
46
47
  void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
47
48
  VALUE e;
48
49
  GET_CLIENT(stmt_wrapper->client);
@@ -71,7 +72,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
71
72
  rb_exc_raise(e);
72
73
  }
73
74
 
74
-
75
75
  /*
76
76
  * used to pass all arguments to mysql_stmt_prepare while inside
77
77
  * nogvl_prepare_statement_args
@@ -124,7 +124,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
124
124
 
125
125
  // set STMT_ATTR_UPDATE_MAX_LENGTH attr
126
126
  {
127
- my_bool truth = 1;
127
+ bool truth = 1;
128
128
  if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
129
129
  rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
130
130
  }
@@ -180,14 +180,16 @@ static void *nogvl_execute(void *ptr) {
180
180
  }
181
181
  }
182
182
 
183
- static void *nogvl_stmt_store_result(void *ptr) {
184
- MYSQL_STMT *stmt = ptr;
183
+ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
184
+ unsigned long length;
185
185
 
186
- if (mysql_stmt_store_result(stmt)) {
187
- return (void *)Qfalse;
188
- } else {
189
- return (void *)Qtrue;
190
- }
186
+ bind_buffer->buffer = RSTRING_PTR(string);
187
+
188
+ length = RSTRING_LEN(string);
189
+ bind_buffer->buffer_length = length;
190
+ *length_buffer = length;
191
+
192
+ bind_buffer->length = length_buffer;
191
193
  }
192
194
 
193
195
  /* Free each bind_buffer[i].buffer except when params_enc is non-nil, this means
@@ -204,6 +206,50 @@ static void *nogvl_stmt_store_result(void *ptr) {
204
206
  xfree(length_buffers); \
205
207
  }
206
208
 
209
+ /* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
210
+ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
211
+ {
212
+ unsigned LONG_LONG num;
213
+ size_t len;
214
+ #ifdef HAVE_RB_ABSINT_SIZE
215
+ int nlz_bits = 0;
216
+ len = rb_absint_size(bignum, &nlz_bits);
217
+ #else
218
+ len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS;
219
+ #endif
220
+ if (len > sizeof(LONG_LONG)) goto overflow;
221
+ if (RBIGNUM_POSITIVE_P(bignum)) {
222
+ num = rb_big2ull(bignum);
223
+ if (num > LLONG_MAX)
224
+ goto overflow;
225
+ *ptr = num;
226
+ }
227
+ else {
228
+ if (len == 8 &&
229
+ #ifdef HAVE_RB_ABSINT_SIZE
230
+ nlz_bits == 0 &&
231
+ #endif
232
+ #if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
233
+ /* Optimized to avoid object allocation for Ruby 2.1+
234
+ * only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
235
+ */
236
+ !rb_absint_singlebit_p(bignum)
237
+ #elif defined(HAVE_RB_BIG_CMP)
238
+ rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
239
+ #else
240
+ /* Ruby 1.8.7 and REE doesn't have rb_big_cmp */
241
+ rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
242
+ #endif
243
+ ) {
244
+ goto overflow;
245
+ }
246
+ *ptr = rb_big2ll(bignum);
247
+ }
248
+ return 0;
249
+ overflow:
250
+ return 1;
251
+ }
252
+
207
253
  /* call-seq: stmt.execute
208
254
  *
209
255
  * Executes the current prepared statement, returns +result+.
@@ -265,9 +311,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
265
311
  #endif
266
312
  break;
267
313
  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]);
314
+ {
315
+ LONG_LONG num;
316
+ if (my_big2ll(argv[i], &num) == 0) {
317
+ bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
318
+ bind_buffers[i].buffer = xmalloc(sizeof(long long int));
319
+ *(LONG_LONG*)(bind_buffers[i].buffer) = num;
320
+ } else {
321
+ /* 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
+ bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
324
+ params_enc[i] = rb_val_as_string;
325
+ #ifdef HAVE_RUBY_ENCODING_H
326
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
327
+ #endif
328
+ set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
329
+ }
330
+ }
271
331
  break;
272
332
  case T_FLOAT:
273
333
  bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
@@ -275,17 +335,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
275
335
  *(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
276
336
  break;
277
337
  case T_STRING:
278
- {
279
- params_enc[i] = argv[i];
338
+ bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
339
+
340
+ params_enc[i] = argv[i];
280
341
  #ifdef HAVE_RUBY_ENCODING_H
281
- params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
342
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
282
343
  #endif
283
- bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
284
- bind_buffers[i].buffer = RSTRING_PTR(params_enc[i]);
285
- bind_buffers[i].buffer_length = RSTRING_LEN(params_enc[i]);
286
- length_buffers[i] = bind_buffers[i].buffer_length;
287
- bind_buffers[i].length = &length_buffers[i];
288
- }
344
+ set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
345
+ break;
346
+ case T_TRUE:
347
+ bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
348
+ bind_buffers[i].buffer = xmalloc(sizeof(signed char));
349
+ *(signed char*)(bind_buffers[i].buffer) = 1;
350
+ break;
351
+ case T_FALSE:
352
+ bind_buffers[i].buffer_type = MYSQL_TYPE_TINY;
353
+ bind_buffers[i].buffer = xmalloc(sizeof(signed char));
354
+ *(signed char*)(bind_buffers[i].buffer) = 0;
289
355
  break;
290
356
  default:
291
357
  // TODO: what Ruby type should support MYSQL_TYPE_TIME
@@ -298,7 +364,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
298
364
 
299
365
  memset(&t, 0, sizeof(MYSQL_TIME));
300
366
  t.neg = 0;
301
- t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
367
+
368
+ if (CLASS_OF(argv[i]) == rb_cTime) {
369
+ t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
370
+ } else if (CLASS_OF(argv[i]) == cDateTime) {
371
+ t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000;
372
+ }
373
+
302
374
  t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
303
375
  t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
304
376
  t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
@@ -324,6 +396,19 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
324
396
  *(MYSQL_TIME*)(bind_buffers[i].buffer) = t;
325
397
  } else if (CLASS_OF(argv[i]) == cBigDecimal) {
326
398
  bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
399
+
400
+ // DECIMAL are represented with the "string representation of the
401
+ // original server-side value", see
402
+ // https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-statement-type-conversions.html
403
+ // This should be independent of the locale used both on the server
404
+ // and the client side.
405
+ VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
406
+
407
+ params_enc[i] = rb_val_as_string;
408
+ #ifdef HAVE_RUBY_ENCODING_H
409
+ params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
410
+ #endif
411
+ set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
327
412
  }
328
413
  break;
329
414
  }
@@ -347,8 +432,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
347
432
  if (metadata == NULL) {
348
433
  if (mysql_stmt_errno(stmt) != 0) {
349
434
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
350
-
351
- MARK_CONN_INACTIVE(stmt_wrapper->client);
435
+ wrapper->active_thread = Qnil;
352
436
  rb_raise_mysql2_stmt_error(stmt_wrapper);
353
437
  }
354
438
  // no data and no error, so query was not a SELECT
@@ -362,11 +446,11 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
362
446
  is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
363
447
  if (!is_streaming) {
364
448
  // recieve the whole result set from the server
365
- if (rb_thread_call_without_gvl(nogvl_stmt_store_result, stmt, RUBY_UBF_IO, 0) == Qfalse) {
449
+ if (mysql_stmt_store_result(stmt)) {
366
450
  mysql_free_result(metadata);
367
451
  rb_raise_mysql2_stmt_error(stmt_wrapper);
368
452
  }
369
- MARK_CONN_INACTIVE(stmt_wrapper->client);
453
+ wrapper->active_thread = Qnil;
370
454
  }
371
455
 
372
456
  resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
@@ -394,6 +478,7 @@ static VALUE fields(VALUE self) {
394
478
  rb_encoding *default_internal_enc, *conn_enc;
395
479
  #endif
396
480
  GET_STATEMENT(self);
481
+ GET_CLIENT(stmt_wrapper->client);
397
482
  stmt = stmt_wrapper->stmt;
398
483
 
399
484
  #ifdef HAVE_RUBY_ENCODING_H
@@ -404,12 +489,22 @@ static VALUE fields(VALUE self) {
404
489
  }
405
490
  #endif
406
491
 
407
- metadata = mysql_stmt_result_metadata(stmt);
492
+ metadata = mysql_stmt_result_metadata(stmt);
493
+ if (metadata == NULL) {
494
+ if (mysql_stmt_errno(stmt) != 0) {
495
+ // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
496
+ wrapper->active_thread = Qnil;
497
+ rb_raise_mysql2_stmt_error(stmt_wrapper);
498
+ }
499
+ // no data and no error, so query was not a SELECT
500
+ return Qnil;
501
+ }
502
+
408
503
  fields = mysql_fetch_fields(metadata);
409
504
  field_count = mysql_stmt_field_count(stmt);
410
505
  field_list = rb_ary_new2((long)field_count);
411
506
 
412
- for(i = 0; i < field_count; i++) {
507
+ for (i = 0; i < field_count; i++) {
413
508
  VALUE rb_field;
414
509
 
415
510
  rb_field = rb_str_new(fields[i].name, fields[i].name_length);
@@ -484,6 +579,7 @@ void init_mysql2_statement() {
484
579
  intern_new_with_args = rb_intern("new_with_args");
485
580
  intern_each = rb_intern("each");
486
581
 
582
+ intern_sec_fraction = rb_intern("sec_fraction");
487
583
  intern_usec = rb_intern("usec");
488
584
  intern_sec = rb_intern("sec");
489
585
  intern_min = rb_intern("min");
@@ -491,4 +587,9 @@ void init_mysql2_statement() {
491
587
  intern_day = rb_intern("day");
492
588
  intern_month = rb_intern("month");
493
589
  intern_year = rb_intern("year");
590
+
591
+ intern_to_s = rb_intern("to_s");
592
+ #ifndef HAVE_RB_BIG_CMP
593
+ id_cmp = rb_intern("<=>");
594
+ #endif
494
595
  }
data/lib/mysql2/client.rb CHANGED
@@ -19,6 +19,7 @@ module Mysql2
19
19
  end
20
20
 
21
21
  def initialize(opts = {})
22
+ fail 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].each do |key|
34
+ [: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
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].to_i)
40
+ send(:"#{key}=", Integer(opts[key])) unless opts[key].nil?
40
41
  else
41
42
  send(:"#{key}=", opts[key])
42
43
  end
@@ -46,7 +47,8 @@ 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
53
  case opts[:flags]
52
54
  when Array
@@ -60,11 +62,11 @@ module Mysql2
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
67
  if [: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
@@ -87,6 +89,17 @@ module Mysql2
87
89
  connect user, pass, host, port, database, socket, flags
88
90
  end
89
91
 
92
+ def parse_ssl_mode(mode)
93
+ m = mode.to_s.upcase
94
+ if m.start_with?('SSL_MODE_')
95
+ return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
96
+ else
97
+ x = 'SSL_MODE_' + m
98
+ return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
99
+ end
100
+ warn "Unknown MySQL ssl_mode flag: #{mode}"
101
+ end
102
+
90
103
  def parse_flags_array(flags, initial = 0)
91
104
  flags.reduce(initial) do |memo, f|
92
105
  fneg = f.start_with?('-') ? f[1..-1] : nil
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.10"
3
3
  end
@@ -9,9 +9,3 @@ user:
9
9
  username: LOCALUSERNAME
10
10
  password:
11
11
  database: mysql2_test
12
-
13
- numericuser:
14
- host: localhost
15
- username: LOCALUSERNAME
16
- password:
17
- database: 12345
data/spec/em/em_spec.rb CHANGED
@@ -70,6 +70,7 @@ begin
70
70
  let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
71
71
  let(:error) { StandardError.new('some error') }
72
72
  before { allow(client).to receive(:async_result).and_raise(error) }
73
+ after { client.close }
73
74
 
74
75
  it "should swallow exceptions raised in by the client" do
75
76
  errors = []