mysql2 0.4.10 → 0.5.3

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