mysql2 0.4.10 → 0.5.3

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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +66 -32
  3. data/ext/mysql2/client.c +125 -59
  4. data/ext/mysql2/client.h +1 -39
  5. data/ext/mysql2/extconf.rb +24 -21
  6. data/ext/mysql2/mysql2_ext.c +2 -1
  7. data/ext/mysql2/mysql2_ext.h +8 -4
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
  9. data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
  10. data/ext/mysql2/result.c +32 -85
  11. data/ext/mysql2/result.h +2 -3
  12. data/ext/mysql2/statement.c +81 -72
  13. data/ext/mysql2/statement.h +0 -2
  14. data/ext/mysql2/wait_for_single_fd.h +2 -1
  15. data/lib/mysql2.rb +17 -15
  16. data/lib/mysql2/client.rb +33 -27
  17. data/lib/mysql2/em.rb +2 -4
  18. data/lib/mysql2/error.rb +51 -22
  19. data/lib/mysql2/result.rb +2 -0
  20. data/lib/mysql2/statement.rb +3 -9
  21. data/lib/mysql2/version.rb +1 -1
  22. data/support/5072E1F5.asc +5 -5
  23. data/support/mysql_enc_to_ruby.rb +8 -3
  24. data/support/ruby_enc_to_mysql.rb +7 -5
  25. metadata +8 -58
  26. data/examples/eventmachine.rb +0 -21
  27. data/examples/threaded.rb +0 -18
  28. data/spec/configuration.yml.example +0 -11
  29. data/spec/em/em_spec.rb +0 -136
  30. data/spec/my.cnf.example +0 -9
  31. data/spec/mysql2/client_spec.rb +0 -1039
  32. data/spec/mysql2/error_spec.rb +0 -82
  33. data/spec/mysql2/result_spec.rb +0 -545
  34. data/spec/mysql2/statement_spec.rb +0 -776
  35. data/spec/rcov.opts +0 -3
  36. data/spec/spec_helper.rb +0 -108
  37. data/spec/ssl/ca-cert.pem +0 -17
  38. data/spec/ssl/ca-key.pem +0 -27
  39. data/spec/ssl/ca.cnf +0 -22
  40. data/spec/ssl/cert.cnf +0 -22
  41. data/spec/ssl/client-cert.pem +0 -17
  42. data/spec/ssl/client-key.pem +0 -27
  43. data/spec/ssl/client-req.pem +0 -15
  44. data/spec/ssl/gen_certs.sh +0 -48
  45. data/spec/ssl/pkcs8-client-key.pem +0 -28
  46. data/spec/ssl/pkcs8-server-key.pem +0 -28
  47. data/spec/ssl/server-cert.pem +0 -17
  48. data/spec/ssl/server-key.pem +0 -27
  49. data/spec/ssl/server-req.pem +0 -15
  50. data/spec/test_data +0 -1
@@ -1,52 +1,21 @@
1
1
  #include <mysql2_ext.h>
2
2
 
3
3
  #include "mysql_enc_to_ruby.h"
4
+ #define MYSQL2_CHARSETNR_SIZE (sizeof(mysql2_mysql_enc_to_rb)/sizeof(mysql2_mysql_enc_to_rb[0]))
4
5
 
5
- #ifdef HAVE_RUBY_ENCODING_H
6
6
  static rb_encoding *binaryEncoding;
7
- #endif
8
7
 
9
- #if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
10
8
  /* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
11
9
  *
12
10
  * (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
13
11
  */
14
12
  #define MYSQL2_MAX_TIME 315578267999ULL
15
- #else
16
- /**
17
- * On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
18
- * 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds
19
- *
20
- * (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7
21
- */
22
- #define MYSQL2_MAX_TIME 64318634047ULL
23
- #endif
24
13
 
25
- #if defined(HAVE_RUBY_ENCODING_H)
26
14
  /* 0000-1-1 00:00:00 UTC
27
15
  *
28
16
  * (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
29
17
  */
30
18
  #define MYSQL2_MIN_TIME 2678400ULL
31
- #elif SIZEOF_INT < SIZEOF_LONG /* 64bit Ruby 1.8 */
32
- /* 0139-1-1 00:00:00 UTC
33
- *
34
- * (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
35
- */
36
- #define MYSQL2_MIN_TIME 4389184800ULL
37
- #elif defined(NEGATIVE_TIME_T)
38
- /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t.
39
- *
40
- * (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52
41
- */
42
- #define MYSQL2_MIN_TIME 60023299552ULL
43
- #else
44
- /* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t.
45
- *
46
- * (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1
47
- */
48
- #define MYSQL2_MIN_TIME 62171150401ULL
49
- #endif
50
19
 
51
20
  #define GET_RESULT(self) \
52
21
  mysql2_result_wrapper *wrapper; \
@@ -61,17 +30,18 @@ typedef struct {
61
30
  int streaming;
62
31
  ID db_timezone;
63
32
  ID app_timezone;
64
- VALUE block_given;
33
+ int block_given; /* boolean */
65
34
  } result_each_args;
66
35
 
67
- VALUE cBigDecimal, cDateTime, cDate;
68
- static VALUE cMysql2Result;
69
- static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
70
36
  extern VALUE mMysql2, cMysql2Client, cMysql2Error;
71
- static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
72
- static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
73
- sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
74
- static ID intern_merge;
37
+ static VALUE cMysql2Result, cDateTime, cDate;
38
+ static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
39
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
40
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
41
+ intern_query_options;
42
+ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
43
+ sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
44
+ sym_cache_rows, sym_cast, sym_stream, sym_name;
75
45
 
76
46
  /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
77
47
  static void rb_mysql_result_mark(void * wrapper) {
@@ -179,29 +149,19 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
179
149
  rb_field = rb_ary_entry(wrapper->fields, idx);
180
150
  if (rb_field == Qnil) {
181
151
  MYSQL_FIELD *field = NULL;
182
- #ifdef HAVE_RUBY_ENCODING_H
183
152
  rb_encoding *default_internal_enc = rb_default_internal_encoding();
184
153
  rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
185
- #endif
186
154
 
187
155
  field = mysql_fetch_field_direct(wrapper->result, idx);
188
156
  if (symbolize_keys) {
189
- #ifdef HAVE_RB_INTERN3
190
157
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
191
158
  rb_field = ID2SYM(rb_field);
192
- #else
193
- VALUE colStr;
194
- colStr = rb_str_new(field->name, field->name_length);
195
- rb_field = ID2SYM(rb_to_id(colStr));
196
- #endif
197
159
  } else {
198
160
  rb_field = rb_str_new(field->name, field->name_length);
199
- #ifdef HAVE_RUBY_ENCODING_H
200
161
  rb_enc_associate(rb_field, conn_enc);
201
162
  if (default_internal_enc) {
202
163
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
203
164
  }
204
- #endif
205
165
  }
206
166
  rb_ary_store(wrapper->fields, idx, rb_field);
207
167
  }
@@ -209,7 +169,6 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
209
169
  return rb_field;
210
170
  }
211
171
 
212
- #ifdef HAVE_RUBY_ENCODING_H
213
172
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
214
173
  /* if binary flag is set, respect its wishes */
215
174
  if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
@@ -222,7 +181,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
222
181
  const char *enc_name;
223
182
  int enc_index;
224
183
 
225
- enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
184
+ enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
185
+
226
186
  if (enc_name != NULL) {
227
187
  /* use the field encoding we were able to match */
228
188
  enc_index = rb_enc_find_index(enc_name);
@@ -238,7 +198,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
238
198
  }
239
199
  return val;
240
200
  }
241
- #endif
242
201
 
243
202
  /* Interpret microseconds digits left-aligned in fixed-width field.
244
203
  * e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
@@ -262,8 +221,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
262
221
  if (wrapper->result_buffers != NULL) return;
263
222
 
264
223
  wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
265
- wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
266
- wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
224
+ wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
225
+ wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
267
226
  wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
268
227
 
269
228
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -278,12 +237,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
278
237
  wrapper->result_buffers[i].buffer_length = sizeof(signed char);
279
238
  break;
280
239
  case MYSQL_TYPE_SHORT: // short int
240
+ case MYSQL_TYPE_YEAR: // short int
281
241
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
282
242
  wrapper->result_buffers[i].buffer_length = sizeof(short int);
283
243
  break;
284
244
  case MYSQL_TYPE_INT24: // int
285
245
  case MYSQL_TYPE_LONG: // int
286
- case MYSQL_TYPE_YEAR: // int
287
246
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
288
247
  wrapper->result_buffers[i].buffer_length = sizeof(int);
289
248
  break;
@@ -335,16 +294,12 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
335
294
  VALUE rowVal;
336
295
  unsigned int i = 0;
337
296
 
338
- #ifdef HAVE_RUBY_ENCODING_H
339
297
  rb_encoding *default_internal_enc;
340
298
  rb_encoding *conn_enc;
341
- #endif
342
299
  GET_RESULT(self);
343
300
 
344
- #ifdef HAVE_RUBY_ENCODING_H
345
301
  default_internal_enc = rb_default_internal_encoding();
346
302
  conn_enc = rb_to_encoding(wrapper->encoding);
347
- #endif
348
303
 
349
304
  if (wrapper->fields == Qnil) {
350
305
  wrapper->numberOfFields = mysql_num_fields(wrapper->result);
@@ -413,6 +368,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
413
368
  }
414
369
  break;
415
370
  case MYSQL_TYPE_SHORT: // short int
371
+ case MYSQL_TYPE_YEAR: // short int
416
372
  if (result_buffer->is_unsigned) {
417
373
  val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
418
374
  } else {
@@ -421,7 +377,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
421
377
  break;
422
378
  case MYSQL_TYPE_INT24: // int
423
379
  case MYSQL_TYPE_LONG: // int
424
- case MYSQL_TYPE_YEAR: // int
425
380
  if (result_buffer->is_unsigned) {
426
381
  val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
427
382
  } else {
@@ -492,7 +447,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
492
447
  }
493
448
  case MYSQL_TYPE_DECIMAL: // char[]
494
449
  case MYSQL_TYPE_NEWDECIMAL: // char[]
495
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
450
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
496
451
  break;
497
452
  case MYSQL_TYPE_STRING: // char[]
498
453
  case MYSQL_TYPE_VAR_STRING: // char[]
@@ -506,9 +461,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
506
461
  case MYSQL_TYPE_GEOMETRY: // char[]
507
462
  default:
508
463
  val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
509
- #ifdef HAVE_RUBY_ENCODING_H
510
464
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
511
- #endif
512
465
  break;
513
466
  }
514
467
  }
@@ -530,16 +483,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
530
483
  unsigned int i = 0;
531
484
  unsigned long * fieldLengths;
532
485
  void * ptr;
533
- #ifdef HAVE_RUBY_ENCODING_H
534
486
  rb_encoding *default_internal_enc;
535
487
  rb_encoding *conn_enc;
536
- #endif
537
488
  GET_RESULT(self);
538
489
 
539
- #ifdef HAVE_RUBY_ENCODING_H
540
490
  default_internal_enc = rb_default_internal_encoding();
541
491
  conn_enc = rb_to_encoding(wrapper->encoding);
542
- #endif
543
492
 
544
493
  ptr = wrapper->result;
545
494
  row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
@@ -569,9 +518,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
569
518
  val = Qnil;
570
519
  } else {
571
520
  val = rb_str_new(row[i], fieldLengths[i]);
572
- #ifdef HAVE_RUBY_ENCODING_H
573
521
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
574
- #endif
575
522
  }
576
523
  } else {
577
524
  switch(type) {
@@ -602,9 +549,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
602
549
  if (fields[i].decimals == 0) {
603
550
  val = rb_cstr2inum(row[i], 10);
604
551
  } else if (strtod(row[i], NULL) == 0.000000){
605
- val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
552
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
606
553
  }else{
607
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
554
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
608
555
  }
609
556
  break;
610
557
  case MYSQL_TYPE_FLOAT: /* FLOAT field */
@@ -722,9 +669,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
722
669
  case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
723
670
  default:
724
671
  val = rb_str_new(row[i], fieldLengths[i]);
725
- #ifdef HAVE_RUBY_ENCODING_H
726
672
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
727
- #endif
728
673
  break;
729
674
  }
730
675
  }
@@ -751,7 +696,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
751
696
 
752
697
  GET_RESULT(self);
753
698
 
754
- defaults = rb_iv_get(self, "@query_options");
699
+ defaults = rb_ivar_get(self, intern_query_options);
755
700
  Check_Type(defaults, T_HASH);
756
701
  if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
757
702
  symbolizeKeys = 1;
@@ -796,7 +741,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
796
741
  row = fetch_row_func(self, fields, args);
797
742
  if (row != Qnil) {
798
743
  wrapper->numberOfRows++;
799
- if (args->block_given != Qnil) {
744
+ if (args->block_given) {
800
745
  rb_yield(row);
801
746
  }
802
747
  }
@@ -846,7 +791,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
846
791
  return Qnil;
847
792
  }
848
793
 
849
- if (args->block_given != Qnil) {
794
+ if (args->block_given) {
850
795
  rb_yield(row);
851
796
  }
852
797
  }
@@ -864,7 +809,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
864
809
 
865
810
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
866
811
  result_each_args args;
867
- VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
812
+ VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
868
813
  ID db_timezone, app_timezone, dbTz, appTz;
869
814
  int symbolizeKeys, asArray, castBool, cacheRows, cast;
870
815
 
@@ -874,9 +819,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
874
819
  rb_raise(cMysql2Error, "Statement handle already closed");
875
820
  }
876
821
 
877
- defaults = rb_iv_get(self, "@query_options");
822
+ defaults = rb_ivar_get(self, intern_query_options);
878
823
  Check_Type(defaults, T_HASH);
879
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
824
+
825
+ // A block can be passed to this method, but since we don't call the block directly from C,
826
+ // we don't need to capture it into a variable here with the "&" scan arg.
827
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
880
828
  opts = rb_funcall(defaults, intern_merge, 1, opts);
881
829
  } else {
882
830
  opts = defaults;
@@ -942,7 +890,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
942
890
  args.cast = cast;
943
891
  args.db_timezone = db_timezone;
944
892
  args.app_timezone = app_timezone;
945
- args.block_given = block;
893
+ args.block_given = rb_block_given_p();
946
894
 
947
895
  if (wrapper->stmt_wrapper) {
948
896
  fetch_row_func = rb_mysql_result_fetch_row_stmt;
@@ -1007,7 +955,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1007
955
  }
1008
956
 
1009
957
  rb_obj_call_init(obj, 0, NULL);
1010
- rb_iv_set(obj, "@query_options", options);
958
+ rb_ivar_set(obj, intern_query_options, options);
1011
959
 
1012
960
  /* Options that cannot be changed in results.each(...) { |row| }
1013
961
  * should be processed here. */
@@ -1017,7 +965,6 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1017
965
  }
1018
966
 
1019
967
  void init_mysql2_result() {
1020
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1021
968
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1022
969
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1023
970
 
@@ -1036,6 +983,8 @@ void init_mysql2_result() {
1036
983
  intern_local_offset = rb_intern("local_offset");
1037
984
  intern_civil = rb_intern("civil");
1038
985
  intern_new_offset = rb_intern("new_offset");
986
+ intern_BigDecimal = rb_intern("BigDecimal");
987
+ intern_query_options = rb_intern("@query_options");
1039
988
 
1040
989
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1041
990
  sym_as = ID2SYM(rb_intern("as"));
@@ -1058,7 +1007,5 @@ void init_mysql2_result() {
1058
1007
  opt_time_month = INT2NUM(1);
1059
1008
  opt_utc_offset = INT2NUM(0);
1060
1009
 
1061
- #ifdef HAVE_RUBY_ENCODING_H
1062
1010
  binaryEncoding = rb_enc_find("binary");
1063
- #endif
1064
1011
  }
@@ -1,6 +1,5 @@
1
1
  #ifndef MYSQL2_RESULT_H
2
2
  #define MYSQL2_RESULT_H
3
- #include <stdbool.h>
4
3
 
5
4
  void init_mysql2_result(void);
6
5
  VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement);
@@ -22,8 +21,8 @@ typedef struct {
22
21
  mysql_client_wrapper *client_wrapper;
23
22
  /* statement result bind buffers */
24
23
  MYSQL_BIND *result_buffers;
25
- bool *is_null;
26
- bool *error;
24
+ my_bool *is_null;
25
+ my_bool *error;
27
26
  unsigned long *length;
28
27
  } mysql2_result_wrapper;
29
28
 
@@ -1,12 +1,10 @@
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, 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
3
+ extern VALUE mMysql2, cMysql2Error;
4
+ static VALUE cMysql2Statement, cBigDecimal, cDateTime, cDate;
5
+ static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s, intern_merge_bang;
6
+ static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
7
+ intern_query_options;
10
8
 
11
9
  #define GET_STATEMENT(self) \
12
10
  mysql_stmt_wrapper *stmt_wrapper; \
@@ -21,7 +19,7 @@ static void rb_mysql_stmt_mark(void * ptr) {
21
19
  rb_gc_mark(stmt_wrapper->client);
22
20
  }
23
21
 
24
- static void *nogvl_stmt_close(void * ptr) {
22
+ static void *nogvl_stmt_close(void *ptr) {
25
23
  mysql_stmt_wrapper *stmt_wrapper = ptr;
26
24
  if (stmt_wrapper->stmt) {
27
25
  mysql_stmt_close(stmt_wrapper->stmt);
@@ -30,7 +28,7 @@ static void *nogvl_stmt_close(void * ptr) {
30
28
  return NULL;
31
29
  }
32
30
 
33
- static void rb_mysql_stmt_free(void * ptr) {
31
+ static void rb_mysql_stmt_free(void *ptr) {
34
32
  mysql_stmt_wrapper *stmt_wrapper = ptr;
35
33
  decr_mysql2_stmt(stmt_wrapper);
36
34
  }
@@ -50,7 +48,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
50
48
  VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
51
49
  VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
52
50
 
53
- #ifdef HAVE_RUBY_ENCODING_H
54
51
  rb_encoding *conn_enc;
55
52
  conn_enc = rb_to_encoding(wrapper->encoding);
56
53
 
@@ -62,7 +59,6 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
62
59
  rb_error_msg = rb_str_export_to_enc(rb_error_msg, default_internal_enc);
63
60
  rb_sql_state = rb_str_export_to_enc(rb_sql_state, default_internal_enc);
64
61
  }
65
- #endif
66
62
 
67
63
  e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
68
64
  rb_error_msg,
@@ -96,9 +92,7 @@ static void *nogvl_prepare_statement(void *ptr) {
96
92
  VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
97
93
  mysql_stmt_wrapper *stmt_wrapper;
98
94
  VALUE rb_stmt;
99
- #ifdef HAVE_RUBY_ENCODING_H
100
95
  rb_encoding *conn_enc;
101
- #endif
102
96
 
103
97
  Check_Type(sql, T_STRING);
104
98
 
@@ -114,9 +108,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
114
108
  {
115
109
  GET_CLIENT(rb_client);
116
110
  stmt_wrapper->stmt = mysql_stmt_init(wrapper->client);
117
- #ifdef HAVE_RUBY_ENCODING_H
118
111
  conn_enc = rb_to_encoding(wrapper->encoding);
119
- #endif
120
112
  }
121
113
  if (stmt_wrapper->stmt == NULL) {
122
114
  rb_raise(cMysql2Error, "Unable to initialize prepared statement: out of memory");
@@ -124,7 +116,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
124
116
 
125
117
  // set STMT_ATTR_UPDATE_MAX_LENGTH attr
126
118
  {
127
- bool truth = 1;
119
+ my_bool truth = 1;
128
120
  if (mysql_stmt_attr_set(stmt_wrapper->stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &truth)) {
129
121
  rb_raise(cMysql2Error, "Unable to initialize prepared statement: set STMT_ATTR_UPDATE_MAX_LENGTH");
130
122
  }
@@ -134,11 +126,8 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
134
126
  {
135
127
  struct nogvl_prepare_statement_args args;
136
128
  args.stmt = stmt_wrapper->stmt;
137
- args.sql = sql;
138
- #ifdef HAVE_RUBY_ENCODING_H
139
129
  // ensure the string is in the encoding the connection is expecting
140
- args.sql = rb_str_export_to_enc(args.sql, conn_enc);
141
- #endif
130
+ args.sql = rb_str_export_to_enc(sql, conn_enc);
142
131
  args.sql_ptr = RSTRING_PTR(sql);
143
132
  args.sql_len = RSTRING_LEN(sql);
144
133
 
@@ -154,7 +143,7 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
154
143
  *
155
144
  * Returns the number of parameters the prepared statement expects.
156
145
  */
157
- static VALUE param_count(VALUE self) {
146
+ static VALUE rb_mysql_stmt_param_count(VALUE self) {
158
147
  GET_STATEMENT(self);
159
148
 
160
149
  return ULL2NUM(mysql_stmt_param_count(stmt_wrapper->stmt));
@@ -164,13 +153,13 @@ static VALUE param_count(VALUE self) {
164
153
  *
165
154
  * Returns the number of fields the prepared statement returns.
166
155
  */
167
- static VALUE field_count(VALUE self) {
156
+ static VALUE rb_mysql_stmt_field_count(VALUE self) {
168
157
  GET_STATEMENT(self);
169
158
 
170
159
  return UINT2NUM(mysql_stmt_field_count(stmt_wrapper->stmt));
171
160
  }
172
161
 
173
- static void *nogvl_execute(void *ptr) {
162
+ static void *nogvl_stmt_execute(void *ptr) {
174
163
  MYSQL_STMT *stmt = ptr;
175
164
 
176
165
  if (mysql_stmt_execute(stmt)) {
@@ -196,7 +185,7 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
196
185
  * the buffer is a Ruby string pointer and not our memory to manage.
197
186
  */
198
187
  #define FREE_BINDS \
199
- for (i = 0; i < argc; i++) { \
188
+ for (i = 0; i < bind_count; i++) { \
200
189
  if (bind_buffers[i].buffer && NIL_P(params_enc[i])) { \
201
190
  xfree(bind_buffers[i].buffer); \
202
191
  } \
@@ -211,6 +200,8 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
211
200
  {
212
201
  unsigned LONG_LONG num;
213
202
  size_t len;
203
+ // rb_absint_size was added in 2.1.0. See:
204
+ // https://github.com/ruby/ruby/commit/9fea875
214
205
  #ifdef HAVE_RB_ABSINT_SIZE
215
206
  int nlz_bits = 0;
216
207
  len = rb_absint_size(bignum, &nlz_bits);
@@ -229,16 +220,15 @@ static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
229
220
  #ifdef HAVE_RB_ABSINT_SIZE
230
221
  nlz_bits == 0 &&
231
222
  #endif
223
+ // rb_absint_singlebit_p was added in 2.1.0. See:
224
+ // https://github.com/ruby/ruby/commit/e5ff9d5
232
225
  #if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
233
226
  /* Optimized to avoid object allocation for Ruby 2.1+
234
227
  * only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
235
228
  */
236
229
  !rb_absint_singlebit_p(bignum)
237
- #elif defined(HAVE_RB_BIG_CMP)
238
- rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
239
230
  #else
240
- /* Ruby 1.8.7 and REE doesn't have rb_big_cmp */
241
- rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
231
+ rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
242
232
  #endif
243
233
  ) {
244
234
  goto overflow;
@@ -254,44 +244,45 @@ overflow:
254
244
  *
255
245
  * Executes the current prepared statement, returns +result+.
256
246
  */
257
- static VALUE execute(int argc, VALUE *argv, VALUE self) {
247
+ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
258
248
  MYSQL_BIND *bind_buffers = NULL;
259
249
  unsigned long *length_buffers = NULL;
260
250
  unsigned long bind_count;
261
- long i;
251
+ unsigned long i;
262
252
  MYSQL_STMT *stmt;
263
253
  MYSQL_RES *metadata;
254
+ VALUE opts;
264
255
  VALUE current;
265
256
  VALUE resultObj;
266
- VALUE *params_enc;
257
+ VALUE *params_enc = NULL;
267
258
  int is_streaming;
268
- #ifdef HAVE_RUBY_ENCODING_H
269
259
  rb_encoding *conn_enc;
270
- #endif
271
260
 
272
261
  GET_STATEMENT(self);
273
262
  GET_CLIENT(stmt_wrapper->client);
274
263
 
275
- #ifdef HAVE_RUBY_ENCODING_H
276
264
  conn_enc = rb_to_encoding(wrapper->encoding);
277
- #endif
278
-
279
- /* Scratch space for string encoding exports, allocate on the stack. */
280
- params_enc = alloca(sizeof(VALUE) * argc);
281
265
 
282
266
  stmt = stmt_wrapper->stmt;
283
-
284
267
  bind_count = mysql_stmt_param_count(stmt);
285
- if (argc != (long)bind_count) {
286
- rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, argc);
268
+
269
+ // Get count of ordinary arguments, and extract hash opts/keyword arguments
270
+ // Use a local scope to avoid leaking the temporary count variable
271
+ {
272
+ int c = rb_scan_args(argc, argv, "*:", NULL, &opts);
273
+ if (c != (long)bind_count) {
274
+ rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, c);
275
+ }
287
276
  }
288
277
 
289
278
  // setup any bind variables in the query
290
279
  if (bind_count > 0) {
280
+ // Scratch space for string encoding exports, allocate on the stack
281
+ params_enc = alloca(sizeof(VALUE) * bind_count);
291
282
  bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND));
292
283
  length_buffers = xcalloc(bind_count, sizeof(unsigned long));
293
284
 
294
- for (i = 0; i < argc; i++) {
285
+ for (i = 0; i < bind_count; i++) {
295
286
  bind_buffers[i].buffer = NULL;
296
287
  params_enc[i] = Qnil;
297
288
 
@@ -319,12 +310,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
319
310
  *(LONG_LONG*)(bind_buffers[i].buffer) = num;
320
311
  } else {
321
312
  /* The bignum was larger than we can fit in LONG_LONG, send it as a string */
322
- VALUE rb_val_as_string = rb_big2str(argv[i], 10);
323
313
  bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
324
- params_enc[i] = 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
314
+ params_enc[i] = rb_str_export_to_enc(rb_big2str(argv[i], 10), conn_enc);
328
315
  set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
329
316
  }
330
317
  }
@@ -338,9 +325,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
338
325
  bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
339
326
 
340
327
  params_enc[i] = argv[i];
341
- #ifdef HAVE_RUBY_ENCODING_H
342
328
  params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
343
- #endif
344
329
  set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
345
330
  break;
346
331
  case T_TRUE:
@@ -405,9 +390,7 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
405
390
  VALUE rb_val_as_string = rb_funcall(argv[i], intern_to_s, 0);
406
391
 
407
392
  params_enc[i] = rb_val_as_string;
408
- #ifdef HAVE_RUBY_ENCODING_H
409
393
  params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
410
- #endif
411
394
  set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
412
395
  }
413
396
  break;
@@ -421,7 +404,40 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
421
404
  }
422
405
  }
423
406
 
424
- if ((VALUE)rb_thread_call_without_gvl(nogvl_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
407
+ // Duplicate the options hash, merge! extra opts, put the copy into the Result object
408
+ current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
409
+ (void)RB_GC_GUARD(current);
410
+ Check_Type(current, T_HASH);
411
+
412
+ // Merge in hash opts/keyword arguments
413
+ if (!NIL_P(opts)) {
414
+ rb_funcall(current, intern_merge_bang, 1, opts);
415
+ }
416
+
417
+ is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
418
+
419
+ // From stmt_execute to mysql_stmt_result_metadata to stmt_store_result, no
420
+ // Ruby API calls are allowed so that GC is not invoked. If the connection is
421
+ // in results-streaming-mode for Statement A, and in the middle Statement B
422
+ // gets garbage collected, a message will be sent to the server notifying it
423
+ // to release Statement B, resulting in the following error:
424
+ // Commands out of sync; you can't run this command now
425
+ //
426
+ // In streaming mode, statement execute must return a cursor because we
427
+ // cannot prevent other Statement objects from being garbage collected
428
+ // between fetches of each row of the result set. The following error
429
+ // occurs if cursor mode is not set:
430
+ // Row retrieval was canceled by mysql_stmt_close
431
+
432
+ if (is_streaming) {
433
+ unsigned long type = CURSOR_TYPE_READ_ONLY;
434
+ if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, &type)) {
435
+ FREE_BINDS;
436
+ rb_raise(cMysql2Error, "Unable to stream prepared statement, could not set CURSOR_TYPE_READ_ONLY");
437
+ }
438
+ }
439
+
440
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_stmt_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) {
425
441
  FREE_BINDS;
426
442
  rb_raise_mysql2_stmt_error(stmt_wrapper);
427
443
  }
@@ -439,11 +455,6 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
439
455
  return Qnil;
440
456
  }
441
457
 
442
- current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
443
- (void)RB_GC_GUARD(current);
444
- Check_Type(current, T_HASH);
445
-
446
- is_streaming = (Qtrue == rb_hash_aref(current, sym_stream));
447
458
  if (!is_streaming) {
448
459
  // recieve the whole result set from the server
449
460
  if (mysql_stmt_store_result(stmt)) {
@@ -455,6 +466,8 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
455
466
 
456
467
  resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
457
468
 
469
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
470
+
458
471
  if (!is_streaming) {
459
472
  // cache all result
460
473
  rb_funcall(resultObj, intern_each, 0);
@@ -467,27 +480,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
467
480
  *
468
481
  * Returns a list of fields that will be returned by this statement.
469
482
  */
470
- static VALUE fields(VALUE self) {
483
+ static VALUE rb_mysql_stmt_fields(VALUE self) {
471
484
  MYSQL_FIELD *fields;
472
485
  MYSQL_RES *metadata;
473
486
  unsigned int field_count;
474
487
  unsigned int i;
475
488
  VALUE field_list;
476
489
  MYSQL_STMT* stmt;
477
- #ifdef HAVE_RUBY_ENCODING_H
478
490
  rb_encoding *default_internal_enc, *conn_enc;
479
- #endif
480
491
  GET_STATEMENT(self);
481
492
  GET_CLIENT(stmt_wrapper->client);
482
493
  stmt = stmt_wrapper->stmt;
483
494
 
484
- #ifdef HAVE_RUBY_ENCODING_H
485
495
  default_internal_enc = rb_default_internal_encoding();
486
496
  {
487
497
  GET_CLIENT(stmt_wrapper->client);
488
498
  conn_enc = rb_to_encoding(wrapper->encoding);
489
499
  }
490
- #endif
491
500
 
492
501
  metadata = mysql_stmt_result_metadata(stmt);
493
502
  if (metadata == NULL) {
@@ -508,12 +517,10 @@ static VALUE fields(VALUE self) {
508
517
  VALUE rb_field;
509
518
 
510
519
  rb_field = rb_str_new(fields[i].name, fields[i].name_length);
511
- #ifdef HAVE_RUBY_ENCODING_H
512
520
  rb_enc_associate(rb_field, conn_enc);
513
521
  if (default_internal_enc) {
514
522
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
515
523
  }
516
- #endif
517
524
 
518
525
  rb_ary_store(field_list, (long)i, rb_field);
519
526
  }
@@ -564,12 +571,15 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
564
571
  }
565
572
 
566
573
  void init_mysql2_statement() {
567
- cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
574
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
575
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
576
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
568
577
 
569
- rb_define_method(cMysql2Statement, "param_count", param_count, 0);
570
- rb_define_method(cMysql2Statement, "field_count", field_count, 0);
571
- rb_define_method(cMysql2Statement, "_execute", execute, -1);
572
- rb_define_method(cMysql2Statement, "fields", fields, 0);
578
+ cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
579
+ rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
580
+ rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
581
+ rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
582
+ rb_define_method(cMysql2Statement, "fields", rb_mysql_stmt_fields, 0);
573
583
  rb_define_method(cMysql2Statement, "last_id", rb_mysql_stmt_last_id, 0);
574
584
  rb_define_method(cMysql2Statement, "affected_rows", rb_mysql_stmt_affected_rows, 0);
575
585
  rb_define_method(cMysql2Statement, "close", rb_mysql_stmt_close, 0);
@@ -589,7 +599,6 @@ void init_mysql2_statement() {
589
599
  intern_year = rb_intern("year");
590
600
 
591
601
  intern_to_s = rb_intern("to_s");
592
- #ifndef HAVE_RB_BIG_CMP
593
- id_cmp = rb_intern("<=>");
594
- #endif
602
+ intern_merge_bang = rb_intern("merge!");
603
+ intern_query_options = rb_intern("@query_options");
595
604
  }