mysql2 0.4.2 → 0.4.10

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.
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 = []