mysql2 0.5.2 → 0.5.6

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 (48) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +145 -44
  3. data/ext/mysql2/client.c +236 -58
  4. data/ext/mysql2/client.h +9 -2
  5. data/ext/mysql2/extconf.rb +62 -7
  6. data/ext/mysql2/mysql2_ext.c +6 -1
  7. data/ext/mysql2/mysql2_ext.h +13 -0
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -55
  9. data/ext/mysql2/mysql_enc_to_ruby.h +71 -5
  10. data/ext/mysql2/result.c +287 -22
  11. data/ext/mysql2/result.h +1 -0
  12. data/ext/mysql2/statement.c +63 -14
  13. data/lib/mysql2/client.rb +24 -3
  14. data/lib/mysql2/error.rb +4 -3
  15. data/lib/mysql2/statement.rb +1 -3
  16. data/lib/mysql2/version.rb +1 -1
  17. data/lib/mysql2.rb +8 -3
  18. data/support/3A79BD29.asc +49 -0
  19. data/support/5072E1F5.asc +5 -5
  20. data/support/C74CD1D8.asc +104 -0
  21. data/support/mysql_enc_to_ruby.rb +7 -1
  22. data/support/ruby_enc_to_mysql.rb +3 -0
  23. metadata +15 -59
  24. data/examples/eventmachine.rb +0 -19
  25. data/examples/threaded.rb +0 -16
  26. data/spec/configuration.yml.example +0 -11
  27. data/spec/em/em_spec.rb +0 -135
  28. data/spec/my.cnf.example +0 -9
  29. data/spec/mysql2/client_spec.rb +0 -1072
  30. data/spec/mysql2/error_spec.rb +0 -78
  31. data/spec/mysql2/result_spec.rb +0 -485
  32. data/spec/mysql2/statement_spec.rb +0 -712
  33. data/spec/rcov.opts +0 -3
  34. data/spec/spec_helper.rb +0 -112
  35. data/spec/ssl/ca-cert.pem +0 -17
  36. data/spec/ssl/ca-key.pem +0 -27
  37. data/spec/ssl/ca.cnf +0 -22
  38. data/spec/ssl/cert.cnf +0 -22
  39. data/spec/ssl/client-cert.pem +0 -17
  40. data/spec/ssl/client-key.pem +0 -27
  41. data/spec/ssl/client-req.pem +0 -15
  42. data/spec/ssl/gen_certs.sh +0 -48
  43. data/spec/ssl/pkcs8-client-key.pem +0 -28
  44. data/spec/ssl/pkcs8-server-key.pem +0 -28
  45. data/spec/ssl/server-cert.pem +0 -17
  46. data/spec/ssl/server-key.pem +0 -27
  47. data/spec/ssl/server-req.pem +0 -15
  48. data/spec/test_data +0 -1
data/ext/mysql2/result.c CHANGED
@@ -1,6 +1,7 @@
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
6
  static rb_encoding *binaryEncoding;
6
7
 
@@ -16,9 +17,27 @@ static rb_encoding *binaryEncoding;
16
17
  */
17
18
  #define MYSQL2_MIN_TIME 2678400ULL
18
19
 
20
+ #define MYSQL2_MAX_BYTES_PER_CHAR 3
21
+
22
+ /* From Mysql documentations:
23
+ * To distinguish between binary and nonbinary data for string data types,
24
+ * check whether the charsetnr value is 63. If so, the character set is binary,
25
+ * which indicates binary rather than nonbinary data. This enables you to distinguish BINARY
26
+ * from CHAR, VARBINARY from VARCHAR, and the BLOB types from the TEXT types.
27
+ */
28
+ #define MYSQL2_BINARY_CHARSET 63
29
+
30
+ #ifndef MYSQL_TYPE_JSON
31
+ #define MYSQL_TYPE_JSON 245
32
+ #endif
33
+
34
+ #ifndef NEW_TYPEDDATA_WRAPPER
35
+ #define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
36
+ #endif
37
+
19
38
  #define GET_RESULT(self) \
20
39
  mysql2_result_wrapper *wrapper; \
21
- Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
40
+ TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
22
41
 
23
42
  typedef struct {
24
43
  int symbolizeKeys;
@@ -29,14 +48,15 @@ typedef struct {
29
48
  int streaming;
30
49
  ID db_timezone;
31
50
  ID app_timezone;
32
- VALUE block_given;
51
+ int block_given; /* boolean */
33
52
  } result_each_args;
34
53
 
35
54
  extern VALUE mMysql2, cMysql2Client, cMysql2Error;
36
55
  static VALUE cMysql2Result, cDateTime, cDate;
37
56
  static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
38
57
  static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
39
- intern_civil, intern_new_offset, intern_merge, intern_BigDecimal;
58
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
59
+ intern_query_options;
40
60
  static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
41
61
  sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
42
62
  sym_cache_rows, sym_cast, sym_stream, sym_name;
@@ -45,11 +65,11 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
45
65
  static void rb_mysql_result_mark(void * wrapper) {
46
66
  mysql2_result_wrapper * w = wrapper;
47
67
  if (w) {
48
- rb_gc_mark(w->fields);
49
- rb_gc_mark(w->rows);
50
- rb_gc_mark(w->encoding);
51
- rb_gc_mark(w->client);
52
- rb_gc_mark(w->statement);
68
+ rb_gc_mark_movable(w->fields);
69
+ rb_gc_mark_movable(w->rows);
70
+ rb_gc_mark_movable(w->encoding);
71
+ rb_gc_mark_movable(w->client);
72
+ rb_gc_mark_movable(w->statement);
53
73
  }
54
74
  }
55
75
 
@@ -111,6 +131,48 @@ static void rb_mysql_result_free(void *ptr) {
111
131
  xfree(wrapper);
112
132
  }
113
133
 
134
+ static size_t rb_mysql_result_memsize(const void * wrapper) {
135
+ const mysql2_result_wrapper * w = wrapper;
136
+ size_t memsize = sizeof(*w);
137
+ if (w->stmt_wrapper) {
138
+ memsize += sizeof(*w->stmt_wrapper);
139
+ }
140
+ if (w->client_wrapper) {
141
+ memsize += sizeof(*w->client_wrapper);
142
+ }
143
+ return memsize;
144
+ }
145
+
146
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
147
+ static void rb_mysql_result_compact(void * wrapper) {
148
+ mysql2_result_wrapper * w = wrapper;
149
+ if (w) {
150
+ rb_mysql2_gc_location(w->fields);
151
+ rb_mysql2_gc_location(w->rows);
152
+ rb_mysql2_gc_location(w->encoding);
153
+ rb_mysql2_gc_location(w->client);
154
+ rb_mysql2_gc_location(w->statement);
155
+ }
156
+ }
157
+ #endif
158
+
159
+ static const rb_data_type_t rb_mysql_result_type = {
160
+ "rb_mysql_result",
161
+ {
162
+ rb_mysql_result_mark,
163
+ rb_mysql_result_free,
164
+ rb_mysql_result_memsize,
165
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
166
+ rb_mysql_result_compact,
167
+ #endif
168
+ },
169
+ 0,
170
+ 0,
171
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
172
+ RUBY_TYPED_FREE_IMMEDIATELY,
173
+ #endif
174
+ };
175
+
114
176
  static VALUE rb_mysql_result_free_(VALUE self) {
115
177
  GET_RESULT(self);
116
178
  rb_mysql_result_free_result(wrapper);
@@ -155,11 +217,18 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
155
217
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
156
218
  rb_field = ID2SYM(rb_field);
157
219
  } else {
158
- rb_field = rb_str_new(field->name, field->name_length);
159
- rb_enc_associate(rb_field, conn_enc);
160
- if (default_internal_enc) {
220
+ #ifdef HAVE_RB_ENC_INTERNED_STR
221
+ rb_field = rb_enc_interned_str(field->name, field->name_length, conn_enc);
222
+ if (default_internal_enc && default_internal_enc != conn_enc) {
223
+ rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
224
+ }
225
+ #else
226
+ rb_field = rb_enc_str_new(field->name, field->name_length, conn_enc);
227
+ if (default_internal_enc && default_internal_enc != conn_enc) {
161
228
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
162
229
  }
230
+ rb_obj_freeze(rb_field);
231
+ #endif
163
232
  }
164
233
  rb_ary_store(wrapper->fields, idx, rb_field);
165
234
  }
@@ -167,9 +236,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
167
236
  return rb_field;
168
237
  }
169
238
 
239
+ static VALUE rb_mysql_result_fetch_field_type(VALUE self, unsigned int idx) {
240
+ VALUE rb_field_type;
241
+ GET_RESULT(self);
242
+
243
+ if (wrapper->fieldTypes == Qnil) {
244
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
245
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
246
+ }
247
+
248
+ rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
249
+ if (rb_field_type == Qnil) {
250
+ MYSQL_FIELD *field = NULL;
251
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
252
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
253
+ int precision;
254
+
255
+ field = mysql_fetch_field_direct(wrapper->result, idx);
256
+
257
+ switch(field->type) {
258
+ case MYSQL_TYPE_NULL: // NULL
259
+ rb_field_type = rb_str_new_cstr("null");
260
+ break;
261
+ case MYSQL_TYPE_TINY: // signed char
262
+ rb_field_type = rb_sprintf("tinyint(%ld)", field->length);
263
+ break;
264
+ case MYSQL_TYPE_SHORT: // short int
265
+ rb_field_type = rb_sprintf("smallint(%ld)", field->length);
266
+ break;
267
+ case MYSQL_TYPE_YEAR: // short int
268
+ rb_field_type = rb_sprintf("year(%ld)", field->length);
269
+ break;
270
+ case MYSQL_TYPE_INT24: // int
271
+ rb_field_type = rb_sprintf("mediumint(%ld)", field->length);
272
+ break;
273
+ case MYSQL_TYPE_LONG: // int
274
+ rb_field_type = rb_sprintf("int(%ld)", field->length);
275
+ break;
276
+ case MYSQL_TYPE_LONGLONG: // long long int
277
+ rb_field_type = rb_sprintf("bigint(%ld)", field->length);
278
+ break;
279
+ case MYSQL_TYPE_FLOAT: // float
280
+ rb_field_type = rb_sprintf("float(%ld,%d)", field->length, field->decimals);
281
+ break;
282
+ case MYSQL_TYPE_DOUBLE: // double
283
+ rb_field_type = rb_sprintf("double(%ld,%d)", field->length, field->decimals);
284
+ break;
285
+ case MYSQL_TYPE_TIME: // MYSQL_TIME
286
+ rb_field_type = rb_str_new_cstr("time");
287
+ break;
288
+ case MYSQL_TYPE_DATE: // MYSQL_TIME
289
+ case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
290
+ rb_field_type = rb_str_new_cstr("date");
291
+ break;
292
+ case MYSQL_TYPE_DATETIME: // MYSQL_TIME
293
+ rb_field_type = rb_str_new_cstr("datetime");
294
+ break;
295
+ case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
296
+ rb_field_type = rb_str_new_cstr("timestamp");
297
+ break;
298
+ case MYSQL_TYPE_DECIMAL: // char[]
299
+ case MYSQL_TYPE_NEWDECIMAL: // char[]
300
+ /*
301
+ Handle precision similar to this line from mysql's code:
302
+ https://github.com/mysql/mysql-server/blob/ea7d2e2d16ac03afdd9cb72a972a95981107bf51/sql/field.cc#L2246
303
+ */
304
+ precision = field->length - (field->decimals > 0 ? 2 : 1);
305
+ rb_field_type = rb_sprintf("decimal(%d,%d)", precision, field->decimals);
306
+ break;
307
+ case MYSQL_TYPE_STRING: // char[]
308
+ if (field->flags & ENUM_FLAG) {
309
+ rb_field_type = rb_str_new_cstr("enum");
310
+ } else if (field->flags & SET_FLAG) {
311
+ rb_field_type = rb_str_new_cstr("set");
312
+ } else {
313
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
314
+ rb_field_type = rb_sprintf("binary(%ld)", field->length);
315
+ } else {
316
+ rb_field_type = rb_sprintf("char(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
317
+ }
318
+ }
319
+ break;
320
+ case MYSQL_TYPE_VAR_STRING: // char[]
321
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
322
+ rb_field_type = rb_sprintf("varbinary(%ld)", field->length);
323
+ } else {
324
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
325
+ }
326
+ break;
327
+ case MYSQL_TYPE_VARCHAR: // char[]
328
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
329
+ break;
330
+ case MYSQL_TYPE_TINY_BLOB: // char[]
331
+ rb_field_type = rb_str_new_cstr("tinyblob");
332
+ break;
333
+ case MYSQL_TYPE_BLOB: // char[]
334
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
335
+ switch(field->length) {
336
+ case 255:
337
+ rb_field_type = rb_str_new_cstr("tinyblob");
338
+ break;
339
+ case 65535:
340
+ rb_field_type = rb_str_new_cstr("blob");
341
+ break;
342
+ case 16777215:
343
+ rb_field_type = rb_str_new_cstr("mediumblob");
344
+ break;
345
+ case 4294967295:
346
+ rb_field_type = rb_str_new_cstr("longblob");
347
+ default:
348
+ break;
349
+ }
350
+ } else {
351
+ if (field->length == (255 * MYSQL2_MAX_BYTES_PER_CHAR)) {
352
+ rb_field_type = rb_str_new_cstr("tinytext");
353
+ } else if (field->length == (65535 * MYSQL2_MAX_BYTES_PER_CHAR)) {
354
+ rb_field_type = rb_str_new_cstr("text");
355
+ } else if (field->length == (16777215 * MYSQL2_MAX_BYTES_PER_CHAR)) {
356
+ rb_field_type = rb_str_new_cstr("mediumtext");
357
+ } else if (field->length == 4294967295) {
358
+ rb_field_type = rb_str_new_cstr("longtext");
359
+ } else {
360
+ rb_field_type = rb_sprintf("text(%ld)", field->length);
361
+ }
362
+ }
363
+ break;
364
+ case MYSQL_TYPE_MEDIUM_BLOB: // char[]
365
+ rb_field_type = rb_str_new_cstr("mediumblob");
366
+ break;
367
+ case MYSQL_TYPE_LONG_BLOB: // char[]
368
+ rb_field_type = rb_str_new_cstr("longblob");
369
+ break;
370
+ case MYSQL_TYPE_BIT: // char[]
371
+ rb_field_type = rb_sprintf("bit(%ld)", field->length);
372
+ break;
373
+ case MYSQL_TYPE_SET: // char[]
374
+ rb_field_type = rb_str_new_cstr("set");
375
+ break;
376
+ case MYSQL_TYPE_ENUM: // char[]
377
+ rb_field_type = rb_str_new_cstr("enum");
378
+ break;
379
+ case MYSQL_TYPE_GEOMETRY: // char[]
380
+ rb_field_type = rb_str_new_cstr("geometry");
381
+ break;
382
+ case MYSQL_TYPE_JSON: // json
383
+ rb_field_type = rb_str_new_cstr("json");
384
+ break;
385
+ default:
386
+ rb_field_type = rb_str_new_cstr("unknown");
387
+ break;
388
+ }
389
+
390
+ rb_enc_associate(rb_field_type, conn_enc);
391
+ if (default_internal_enc) {
392
+ rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
393
+ }
394
+
395
+ rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
396
+ }
397
+
398
+ return rb_field_type;
399
+ }
400
+
170
401
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
171
402
  /* if binary flag is set, respect its wishes */
172
- if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
403
+ if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
173
404
  rb_enc_associate(val, binaryEncoding);
174
405
  } else if (!field.charsetnr) {
175
406
  /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
@@ -179,8 +410,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
179
410
  const char *enc_name;
180
411
  int enc_index;
181
412
 
182
- enc_name = (field.charsetnr-1 < CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
183
-
413
+ enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
414
+
184
415
  if (enc_name != NULL) {
185
416
  /* use the field encoding we were able to match */
186
417
  enc_index = rb_enc_find_index(enc_name);
@@ -694,7 +925,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
694
925
 
695
926
  GET_RESULT(self);
696
927
 
697
- defaults = rb_iv_get(self, "@query_options");
928
+ defaults = rb_ivar_get(self, intern_query_options);
698
929
  Check_Type(defaults, T_HASH);
699
930
  if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
700
931
  symbolizeKeys = 1;
@@ -714,6 +945,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
714
945
  return wrapper->fields;
715
946
  }
716
947
 
948
+ static VALUE rb_mysql_result_fetch_field_types(VALUE self) {
949
+ unsigned int i = 0;
950
+
951
+ GET_RESULT(self);
952
+
953
+ if (wrapper->fieldTypes == Qnil) {
954
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
955
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
956
+ }
957
+
958
+ if ((my_ulonglong)RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
959
+ for (i=0; i<wrapper->numberOfFields; i++) {
960
+ rb_mysql_result_fetch_field_type(self, i);
961
+ }
962
+ }
963
+
964
+ return wrapper->fieldTypes;
965
+ }
966
+
717
967
  static VALUE rb_mysql_result_each_(VALUE self,
718
968
  VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
719
969
  const result_each_args *args)
@@ -739,7 +989,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
739
989
  row = fetch_row_func(self, fields, args);
740
990
  if (row != Qnil) {
741
991
  wrapper->numberOfRows++;
742
- if (args->block_given != Qnil) {
992
+ if (args->block_given) {
743
993
  rb_yield(row);
744
994
  }
745
995
  }
@@ -789,7 +1039,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
789
1039
  return Qnil;
790
1040
  }
791
1041
 
792
- if (args->block_given != Qnil) {
1042
+ if (args->block_given) {
793
1043
  rb_yield(row);
794
1044
  }
795
1045
  }
@@ -807,7 +1057,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
807
1057
 
808
1058
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
809
1059
  result_each_args args;
810
- VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
1060
+ VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
811
1061
  ID db_timezone, app_timezone, dbTz, appTz;
812
1062
  int symbolizeKeys, asArray, castBool, cacheRows, cast;
813
1063
 
@@ -817,9 +1067,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
817
1067
  rb_raise(cMysql2Error, "Statement handle already closed");
818
1068
  }
819
1069
 
820
- defaults = rb_iv_get(self, "@query_options");
1070
+ defaults = rb_ivar_get(self, intern_query_options);
821
1071
  Check_Type(defaults, T_HASH);
822
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
1072
+
1073
+ // A block can be passed to this method, but since we don't call the block directly from C,
1074
+ // we don't need to capture it into a variable here with the "&" scan arg.
1075
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
823
1076
  opts = rb_funcall(defaults, intern_merge, 1, opts);
824
1077
  } else {
825
1078
  opts = defaults;
@@ -885,7 +1138,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
885
1138
  args.cast = cast;
886
1139
  args.db_timezone = db_timezone;
887
1140
  args.app_timezone = app_timezone;
888
- args.block_given = block;
1141
+ args.block_given = rb_block_given_p();
889
1142
 
890
1143
  if (wrapper->stmt_wrapper) {
891
1144
  fetch_row_func = rb_mysql_result_fetch_row_stmt;
@@ -922,13 +1175,18 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
922
1175
  VALUE obj;
923
1176
  mysql2_result_wrapper * wrapper;
924
1177
 
1178
+ #ifdef NEW_TYPEDDATA_WRAPPER
1179
+ obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1180
+ #else
925
1181
  obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1182
+ #endif
926
1183
  wrapper->numberOfFields = 0;
927
1184
  wrapper->numberOfRows = 0;
928
1185
  wrapper->lastRowProcessed = 0;
929
1186
  wrapper->resultFreed = 0;
930
1187
  wrapper->result = r;
931
1188
  wrapper->fields = Qnil;
1189
+ wrapper->fieldTypes = Qnil;
932
1190
  wrapper->rows = Qnil;
933
1191
  wrapper->encoding = encoding;
934
1192
  wrapper->streamingComplete = 0;
@@ -950,7 +1208,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
950
1208
  }
951
1209
 
952
1210
  rb_obj_call_init(obj, 0, NULL);
953
- rb_iv_set(obj, "@query_options", options);
1211
+ rb_ivar_set(obj, intern_query_options, options);
954
1212
 
955
1213
  /* Options that cannot be changed in results.each(...) { |row| }
956
1214
  * should be processed here. */
@@ -961,11 +1219,17 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
961
1219
 
962
1220
  void init_mysql2_result() {
963
1221
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1222
+ rb_global_variable(&cDate);
964
1223
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1224
+ rb_global_variable(&cDateTime);
965
1225
 
966
1226
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1227
+ rb_undef_alloc_func(cMysql2Result);
1228
+ rb_global_variable(&cMysql2Result);
1229
+
967
1230
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
968
1231
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1232
+ rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);
969
1233
  rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
970
1234
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
971
1235
  rb_define_alias(cMysql2Result, "size", "count");
@@ -979,6 +1243,7 @@ void init_mysql2_result() {
979
1243
  intern_civil = rb_intern("civil");
980
1244
  intern_new_offset = rb_intern("new_offset");
981
1245
  intern_BigDecimal = rb_intern("BigDecimal");
1246
+ intern_query_options = rb_intern("@query_options");
982
1247
 
983
1248
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
984
1249
  sym_as = ID2SYM(rb_intern("as"));
data/ext/mysql2/result.h CHANGED
@@ -6,6 +6,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
6
6
 
7
7
  typedef struct {
8
8
  VALUE fields;
9
+ VALUE fieldTypes;
9
10
  VALUE rows;
10
11
  VALUE client;
11
12
  VALUE encoding;
@@ -3,11 +3,16 @@
3
3
  extern VALUE mMysql2, cMysql2Error;
4
4
  static VALUE cMysql2Statement, cBigDecimal, cDateTime, cDate;
5
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;
6
+ static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year,
7
+ intern_query_options;
8
+
9
+ #ifndef NEW_TYPEDDATA_WRAPPER
10
+ #define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
11
+ #endif
7
12
 
8
13
  #define GET_STATEMENT(self) \
9
14
  mysql_stmt_wrapper *stmt_wrapper; \
10
- Data_Get_Struct(self, mysql_stmt_wrapper, stmt_wrapper); \
15
+ TypedData_Get_Struct(self, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper); \
11
16
  if (!stmt_wrapper->stmt) { rb_raise(cMysql2Error, "Invalid statement handle"); } \
12
17
  if (stmt_wrapper->closed) { rb_raise(cMysql2Error, "Statement handle already closed"); }
13
18
 
@@ -15,9 +20,45 @@ static void rb_mysql_stmt_mark(void * ptr) {
15
20
  mysql_stmt_wrapper *stmt_wrapper = ptr;
16
21
  if (!stmt_wrapper) return;
17
22
 
18
- rb_gc_mark(stmt_wrapper->client);
23
+ rb_gc_mark_movable(stmt_wrapper->client);
24
+ }
25
+
26
+ static void rb_mysql_stmt_free(void *ptr) {
27
+ mysql_stmt_wrapper *stmt_wrapper = ptr;
28
+ decr_mysql2_stmt(stmt_wrapper);
19
29
  }
20
30
 
31
+ static size_t rb_mysql_stmt_memsize(const void * ptr) {
32
+ const mysql_stmt_wrapper *stmt_wrapper = ptr;
33
+ return sizeof(*stmt_wrapper);
34
+ }
35
+
36
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
37
+ static void rb_mysql_stmt_compact(void * ptr) {
38
+ mysql_stmt_wrapper *stmt_wrapper = ptr;
39
+ if (!stmt_wrapper) return;
40
+
41
+ rb_mysql2_gc_location(stmt_wrapper->client);
42
+ }
43
+ #endif
44
+
45
+ static const rb_data_type_t rb_mysql_statement_type = {
46
+ "rb_mysql_statement",
47
+ {
48
+ rb_mysql_stmt_mark,
49
+ rb_mysql_stmt_free,
50
+ rb_mysql_stmt_memsize,
51
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
52
+ rb_mysql_stmt_compact,
53
+ #endif
54
+ },
55
+ 0,
56
+ 0,
57
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
58
+ RUBY_TYPED_FREE_IMMEDIATELY,
59
+ #endif
60
+ };
61
+
21
62
  static void *nogvl_stmt_close(void *ptr) {
22
63
  mysql_stmt_wrapper *stmt_wrapper = ptr;
23
64
  if (stmt_wrapper->stmt) {
@@ -27,11 +68,6 @@ static void *nogvl_stmt_close(void *ptr) {
27
68
  return NULL;
28
69
  }
29
70
 
30
- static void rb_mysql_stmt_free(void *ptr) {
31
- mysql_stmt_wrapper *stmt_wrapper = ptr;
32
- decr_mysql2_stmt(stmt_wrapper);
33
- }
34
-
35
71
  void decr_mysql2_stmt(mysql_stmt_wrapper *stmt_wrapper) {
36
72
  stmt_wrapper->refcount--;
37
73
 
@@ -45,7 +81,7 @@ void rb_raise_mysql2_stmt_error(mysql_stmt_wrapper *stmt_wrapper) {
45
81
  VALUE e;
46
82
  GET_CLIENT(stmt_wrapper->client);
47
83
  VALUE rb_error_msg = rb_str_new2(mysql_stmt_error(stmt_wrapper->stmt));
48
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
84
+ VALUE rb_sql_state = rb_str_new2(mysql_stmt_sqlstate(stmt_wrapper->stmt));
49
85
 
50
86
  rb_encoding *conn_enc;
51
87
  conn_enc = rb_to_encoding(wrapper->encoding);
@@ -95,7 +131,11 @@ VALUE rb_mysql_stmt_new(VALUE rb_client, VALUE sql) {
95
131
 
96
132
  Check_Type(sql, T_STRING);
97
133
 
134
+ #ifdef NEW_TYPEDDATA_WRAPPER
135
+ rb_stmt = TypedData_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, &rb_mysql_statement_type, stmt_wrapper);
136
+ #else
98
137
  rb_stmt = Data_Make_Struct(cMysql2Statement, mysql_stmt_wrapper, rb_mysql_stmt_mark, rb_mysql_stmt_free, stmt_wrapper);
138
+ #endif
99
139
  {
100
140
  stmt_wrapper->client = rb_client;
101
141
  stmt_wrapper->refcount = 1;
@@ -404,7 +444,7 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
404
444
  }
405
445
 
406
446
  // Duplicate the options hash, merge! extra opts, put the copy into the Result object
407
- current = rb_hash_dup(rb_iv_get(stmt_wrapper->client, "@query_options"));
447
+ current = rb_hash_dup(rb_ivar_get(stmt_wrapper->client, intern_query_options));
408
448
  (void)RB_GC_GUARD(current);
409
449
  Check_Type(current, T_HASH);
410
450
 
@@ -447,7 +487,7 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
447
487
  if (metadata == NULL) {
448
488
  if (mysql_stmt_errno(stmt) != 0) {
449
489
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
450
- wrapper->active_thread = Qnil;
490
+ wrapper->active_fiber = Qnil;
451
491
  rb_raise_mysql2_stmt_error(stmt_wrapper);
452
492
  }
453
493
  // no data and no error, so query was not a SELECT
@@ -455,12 +495,12 @@ static VALUE rb_mysql_stmt_execute(int argc, VALUE *argv, VALUE self) {
455
495
  }
456
496
 
457
497
  if (!is_streaming) {
458
- // recieve the whole result set from the server
498
+ // receive the whole result set from the server
459
499
  if (mysql_stmt_store_result(stmt)) {
460
500
  mysql_free_result(metadata);
461
501
  rb_raise_mysql2_stmt_error(stmt_wrapper);
462
502
  }
463
- wrapper->active_thread = Qnil;
503
+ wrapper->active_fiber = Qnil;
464
504
  }
465
505
 
466
506
  resultObj = rb_mysql_result_to_obj(stmt_wrapper->client, wrapper->encoding, current, metadata, self);
@@ -501,7 +541,7 @@ static VALUE rb_mysql_stmt_fields(VALUE self) {
501
541
  if (metadata == NULL) {
502
542
  if (mysql_stmt_errno(stmt) != 0) {
503
543
  // either CR_OUT_OF_MEMORY or CR_UNKNOWN_ERROR. both fatal.
504
- wrapper->active_thread = Qnil;
544
+ wrapper->active_fiber = Qnil;
505
545
  rb_raise_mysql2_stmt_error(stmt_wrapper);
506
546
  }
507
547
  // no data and no error, so query was not a SELECT
@@ -571,10 +611,18 @@ static VALUE rb_mysql_stmt_close(VALUE self) {
571
611
 
572
612
  void init_mysql2_statement() {
573
613
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
614
+ rb_global_variable(&cDate);
615
+
574
616
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
617
+ rb_global_variable(&cDateTime);
618
+
575
619
  cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
620
+ rb_global_variable(&cBigDecimal);
576
621
 
577
622
  cMysql2Statement = rb_define_class_under(mMysql2, "Statement", rb_cObject);
623
+ rb_undef_alloc_func(cMysql2Statement);
624
+ rb_global_variable(&cMysql2Statement);
625
+
578
626
  rb_define_method(cMysql2Statement, "param_count", rb_mysql_stmt_param_count, 0);
579
627
  rb_define_method(cMysql2Statement, "field_count", rb_mysql_stmt_field_count, 0);
580
628
  rb_define_method(cMysql2Statement, "_execute", rb_mysql_stmt_execute, -1);
@@ -599,4 +647,5 @@ void init_mysql2_statement() {
599
647
 
600
648
  intern_to_s = rb_intern("to_s");
601
649
  intern_merge_bang = rb_intern("merge!");
650
+ intern_query_options = rb_intern("@query_options");
602
651
  }