mysql2 0.4.10 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +98 -38
  3. data/ext/mysql2/client.c +223 -76
  4. data/ext/mysql2/client.h +1 -39
  5. data/ext/mysql2/extconf.rb +46 -26
  6. data/ext/mysql2/mysql2_ext.c +8 -2
  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 +242 -86
  11. data/ext/mysql2/result.h +3 -3
  12. data/ext/mysql2/statement.c +90 -73
  13. data/ext/mysql2/statement.h +0 -2
  14. data/ext/mysql2/wait_for_single_fd.h +2 -1
  15. data/lib/mysql2/client.rb +51 -28
  16. data/lib/mysql2/em.rb +2 -4
  17. data/lib/mysql2/error.rb +52 -22
  18. data/lib/mysql2/result.rb +2 -0
  19. data/lib/mysql2/statement.rb +3 -11
  20. data/lib/mysql2/version.rb +1 -1
  21. data/lib/mysql2.rb +18 -15
  22. data/support/3A79BD29.asc +49 -0
  23. data/support/5072E1F5.asc +5 -5
  24. data/support/mysql_enc_to_ruby.rb +8 -3
  25. data/support/ruby_enc_to_mysql.rb +7 -5
  26. metadata +14 -58
  27. data/examples/eventmachine.rb +0 -21
  28. data/examples/threaded.rb +0 -18
  29. data/spec/configuration.yml.example +0 -11
  30. data/spec/em/em_spec.rb +0 -136
  31. data/spec/my.cnf.example +0 -9
  32. data/spec/mysql2/client_spec.rb +0 -1039
  33. data/spec/mysql2/error_spec.rb +0 -82
  34. data/spec/mysql2/result_spec.rb +0 -545
  35. data/spec/mysql2/statement_spec.rb +0 -776
  36. data/spec/rcov.opts +0 -3
  37. data/spec/spec_helper.rb +0 -108
  38. data/spec/ssl/ca-cert.pem +0 -17
  39. data/spec/ssl/ca-key.pem +0 -27
  40. data/spec/ssl/ca.cnf +0 -22
  41. data/spec/ssl/cert.cnf +0 -22
  42. data/spec/ssl/client-cert.pem +0 -17
  43. data/spec/ssl/client-key.pem +0 -27
  44. data/spec/ssl/client-req.pem +0 -15
  45. data/spec/ssl/gen_certs.sh +0 -48
  46. data/spec/ssl/pkcs8-client-key.pem +0 -28
  47. data/spec/ssl/pkcs8-server-key.pem +0 -28
  48. data/spec/ssl/server-cert.pem +0 -17
  49. data/spec/ssl/server-key.pem +0 -27
  50. data/spec/ssl/server-req.pem +0 -15
  51. data/spec/test_data +0 -1
data/ext/mysql2/result.c CHANGED
@@ -1,51 +1,34 @@
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
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.
47
27
  */
48
- #define MYSQL2_MIN_TIME 62171150401ULL
28
+ #define MYSQL2_BINARY_CHARSET 63
29
+
30
+ #ifndef MYSQL_TYPE_JSON
31
+ #define MYSQL_TYPE_JSON 245
49
32
  #endif
50
33
 
51
34
  #define GET_RESULT(self) \
@@ -61,17 +44,18 @@ typedef struct {
61
44
  int streaming;
62
45
  ID db_timezone;
63
46
  ID app_timezone;
64
- VALUE block_given;
47
+ int block_given; /* boolean */
65
48
  } result_each_args;
66
49
 
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
50
  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;
51
+ static VALUE cMysql2Result, cDateTime, cDate;
52
+ static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
53
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
54
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
55
+ intern_query_options;
56
+ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
57
+ sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
58
+ sym_cache_rows, sym_cast, sym_stream, sym_name;
75
59
 
76
60
  /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
77
61
  static void rb_mysql_result_mark(void * wrapper) {
@@ -179,28 +163,25 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
179
163
  rb_field = rb_ary_entry(wrapper->fields, idx);
180
164
  if (rb_field == Qnil) {
181
165
  MYSQL_FIELD *field = NULL;
182
- #ifdef HAVE_RUBY_ENCODING_H
183
166
  rb_encoding *default_internal_enc = rb_default_internal_encoding();
184
167
  rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
185
- #endif
186
168
 
187
169
  field = mysql_fetch_field_direct(wrapper->result, idx);
188
170
  if (symbolize_keys) {
189
- #ifdef HAVE_RB_INTERN3
190
171
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
191
172
  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
173
  } else {
198
- rb_field = rb_str_new(field->name, field->name_length);
199
- #ifdef HAVE_RUBY_ENCODING_H
200
- rb_enc_associate(rb_field, conn_enc);
201
- if (default_internal_enc) {
174
+ #ifdef HAVE_RB_ENC_INTERNED_STR
175
+ rb_field = rb_enc_interned_str(field->name, field->name_length, conn_enc);
176
+ if (default_internal_enc && default_internal_enc != conn_enc) {
177
+ rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
178
+ }
179
+ #else
180
+ rb_field = rb_enc_str_new(field->name, field->name_length, conn_enc);
181
+ if (default_internal_enc && default_internal_enc != conn_enc) {
202
182
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
203
183
  }
184
+ rb_obj_freeze(rb_field);
204
185
  #endif
205
186
  }
206
187
  rb_ary_store(wrapper->fields, idx, rb_field);
@@ -209,10 +190,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
209
190
  return rb_field;
210
191
  }
211
192
 
212
- #ifdef HAVE_RUBY_ENCODING_H
193
+ static VALUE rb_mysql_result_fetch_field_type(VALUE self, unsigned int idx) {
194
+ VALUE rb_field_type;
195
+ GET_RESULT(self);
196
+
197
+ if (wrapper->fieldTypes == Qnil) {
198
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
199
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
200
+ }
201
+
202
+ rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
203
+ if (rb_field_type == Qnil) {
204
+ MYSQL_FIELD *field = NULL;
205
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
206
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
207
+ int precision;
208
+
209
+ field = mysql_fetch_field_direct(wrapper->result, idx);
210
+
211
+ switch(field->type) {
212
+ case MYSQL_TYPE_NULL: // NULL
213
+ rb_field_type = rb_str_new_cstr("null");
214
+ break;
215
+ case MYSQL_TYPE_TINY: // signed char
216
+ rb_field_type = rb_sprintf("tinyint(%ld)", field->length);
217
+ break;
218
+ case MYSQL_TYPE_SHORT: // short int
219
+ rb_field_type = rb_sprintf("smallint(%ld)", field->length);
220
+ break;
221
+ case MYSQL_TYPE_YEAR: // short int
222
+ rb_field_type = rb_sprintf("year(%ld)", field->length);
223
+ break;
224
+ case MYSQL_TYPE_INT24: // int
225
+ rb_field_type = rb_sprintf("mediumint(%ld)", field->length);
226
+ break;
227
+ case MYSQL_TYPE_LONG: // int
228
+ rb_field_type = rb_sprintf("int(%ld)", field->length);
229
+ break;
230
+ case MYSQL_TYPE_LONGLONG: // long long int
231
+ rb_field_type = rb_sprintf("bigint(%ld)", field->length);
232
+ break;
233
+ case MYSQL_TYPE_FLOAT: // float
234
+ rb_field_type = rb_sprintf("float(%ld,%d)", field->length, field->decimals);
235
+ break;
236
+ case MYSQL_TYPE_DOUBLE: // double
237
+ rb_field_type = rb_sprintf("double(%ld,%d)", field->length, field->decimals);
238
+ break;
239
+ case MYSQL_TYPE_TIME: // MYSQL_TIME
240
+ rb_field_type = rb_str_new_cstr("time");
241
+ break;
242
+ case MYSQL_TYPE_DATE: // MYSQL_TIME
243
+ case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
244
+ rb_field_type = rb_str_new_cstr("date");
245
+ break;
246
+ case MYSQL_TYPE_DATETIME: // MYSQL_TIME
247
+ rb_field_type = rb_str_new_cstr("datetime");
248
+ break;
249
+ case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
250
+ rb_field_type = rb_str_new_cstr("timestamp");
251
+ break;
252
+ case MYSQL_TYPE_DECIMAL: // char[]
253
+ case MYSQL_TYPE_NEWDECIMAL: // char[]
254
+ /*
255
+ Handle precision similar to this line from mysql's code:
256
+ https://github.com/mysql/mysql-server/blob/ea7d2e2d16ac03afdd9cb72a972a95981107bf51/sql/field.cc#L2246
257
+ */
258
+ precision = field->length - (field->decimals > 0 ? 2 : 1);
259
+ rb_field_type = rb_sprintf("decimal(%d,%d)", precision, field->decimals);
260
+ break;
261
+ case MYSQL_TYPE_STRING: // char[]
262
+ if (field->flags & ENUM_FLAG) {
263
+ rb_field_type = rb_str_new_cstr("enum");
264
+ } else if (field->flags & SET_FLAG) {
265
+ rb_field_type = rb_str_new_cstr("set");
266
+ } else {
267
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
268
+ rb_field_type = rb_sprintf("binary(%ld)", field->length);
269
+ } else {
270
+ rb_field_type = rb_sprintf("char(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
271
+ }
272
+ }
273
+ break;
274
+ case MYSQL_TYPE_VAR_STRING: // char[]
275
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
276
+ rb_field_type = rb_sprintf("varbinary(%ld)", field->length);
277
+ } else {
278
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
279
+ }
280
+ break;
281
+ case MYSQL_TYPE_VARCHAR: // char[]
282
+ rb_field_type = rb_sprintf("varchar(%ld)", field->length / MYSQL2_MAX_BYTES_PER_CHAR);
283
+ break;
284
+ case MYSQL_TYPE_TINY_BLOB: // char[]
285
+ rb_field_type = rb_str_new_cstr("tinyblob");
286
+ break;
287
+ case MYSQL_TYPE_BLOB: // char[]
288
+ if (field->charsetnr == MYSQL2_BINARY_CHARSET) {
289
+ switch(field->length) {
290
+ case 255:
291
+ rb_field_type = rb_str_new_cstr("tinyblob");
292
+ break;
293
+ case 65535:
294
+ rb_field_type = rb_str_new_cstr("blob");
295
+ break;
296
+ case 16777215:
297
+ rb_field_type = rb_str_new_cstr("mediumblob");
298
+ break;
299
+ case 4294967295:
300
+ rb_field_type = rb_str_new_cstr("longblob");
301
+ default:
302
+ break;
303
+ }
304
+ } else {
305
+ if (field->length == (255 * MYSQL2_MAX_BYTES_PER_CHAR)) {
306
+ rb_field_type = rb_str_new_cstr("tinytext");
307
+ } else if (field->length == (65535 * MYSQL2_MAX_BYTES_PER_CHAR)) {
308
+ rb_field_type = rb_str_new_cstr("text");
309
+ } else if (field->length == (16777215 * MYSQL2_MAX_BYTES_PER_CHAR)) {
310
+ rb_field_type = rb_str_new_cstr("mediumtext");
311
+ } else if (field->length == 4294967295) {
312
+ rb_field_type = rb_str_new_cstr("longtext");
313
+ } else {
314
+ rb_field_type = rb_sprintf("text(%ld)", field->length);
315
+ }
316
+ }
317
+ break;
318
+ case MYSQL_TYPE_MEDIUM_BLOB: // char[]
319
+ rb_field_type = rb_str_new_cstr("mediumblob");
320
+ break;
321
+ case MYSQL_TYPE_LONG_BLOB: // char[]
322
+ rb_field_type = rb_str_new_cstr("longblob");
323
+ break;
324
+ case MYSQL_TYPE_BIT: // char[]
325
+ rb_field_type = rb_sprintf("bit(%ld)", field->length);
326
+ break;
327
+ case MYSQL_TYPE_SET: // char[]
328
+ rb_field_type = rb_str_new_cstr("set");
329
+ break;
330
+ case MYSQL_TYPE_ENUM: // char[]
331
+ rb_field_type = rb_str_new_cstr("enum");
332
+ break;
333
+ case MYSQL_TYPE_GEOMETRY: // char[]
334
+ rb_field_type = rb_str_new_cstr("geometry");
335
+ break;
336
+ case MYSQL_TYPE_JSON: // json
337
+ rb_field_type = rb_str_new_cstr("json");
338
+ break;
339
+ default:
340
+ rb_field_type = rb_str_new_cstr("unknown");
341
+ break;
342
+ }
343
+
344
+ rb_enc_associate(rb_field_type, conn_enc);
345
+ if (default_internal_enc) {
346
+ rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
347
+ }
348
+
349
+ rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
350
+ }
351
+
352
+ return rb_field_type;
353
+ }
354
+
213
355
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
214
356
  /* if binary flag is set, respect its wishes */
215
- if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
357
+ if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
216
358
  rb_enc_associate(val, binaryEncoding);
217
359
  } else if (!field.charsetnr) {
218
360
  /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
@@ -222,7 +364,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
222
364
  const char *enc_name;
223
365
  int enc_index;
224
366
 
225
- enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
367
+ enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
368
+
226
369
  if (enc_name != NULL) {
227
370
  /* use the field encoding we were able to match */
228
371
  enc_index = rb_enc_find_index(enc_name);
@@ -238,7 +381,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
238
381
  }
239
382
  return val;
240
383
  }
241
- #endif
242
384
 
243
385
  /* Interpret microseconds digits left-aligned in fixed-width field.
244
386
  * e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
@@ -262,8 +404,8 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
262
404
  if (wrapper->result_buffers != NULL) return;
263
405
 
264
406
  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));
407
+ wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
408
+ wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
267
409
  wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
268
410
 
269
411
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -278,12 +420,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
278
420
  wrapper->result_buffers[i].buffer_length = sizeof(signed char);
279
421
  break;
280
422
  case MYSQL_TYPE_SHORT: // short int
423
+ case MYSQL_TYPE_YEAR: // short int
281
424
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
282
425
  wrapper->result_buffers[i].buffer_length = sizeof(short int);
283
426
  break;
284
427
  case MYSQL_TYPE_INT24: // int
285
428
  case MYSQL_TYPE_LONG: // int
286
- case MYSQL_TYPE_YEAR: // int
287
429
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
288
430
  wrapper->result_buffers[i].buffer_length = sizeof(int);
289
431
  break;
@@ -335,16 +477,12 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
335
477
  VALUE rowVal;
336
478
  unsigned int i = 0;
337
479
 
338
- #ifdef HAVE_RUBY_ENCODING_H
339
480
  rb_encoding *default_internal_enc;
340
481
  rb_encoding *conn_enc;
341
- #endif
342
482
  GET_RESULT(self);
343
483
 
344
- #ifdef HAVE_RUBY_ENCODING_H
345
484
  default_internal_enc = rb_default_internal_encoding();
346
485
  conn_enc = rb_to_encoding(wrapper->encoding);
347
- #endif
348
486
 
349
487
  if (wrapper->fields == Qnil) {
350
488
  wrapper->numberOfFields = mysql_num_fields(wrapper->result);
@@ -413,6 +551,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
413
551
  }
414
552
  break;
415
553
  case MYSQL_TYPE_SHORT: // short int
554
+ case MYSQL_TYPE_YEAR: // short int
416
555
  if (result_buffer->is_unsigned) {
417
556
  val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
418
557
  } else {
@@ -421,7 +560,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
421
560
  break;
422
561
  case MYSQL_TYPE_INT24: // int
423
562
  case MYSQL_TYPE_LONG: // int
424
- case MYSQL_TYPE_YEAR: // int
425
563
  if (result_buffer->is_unsigned) {
426
564
  val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
427
565
  } else {
@@ -492,7 +630,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
492
630
  }
493
631
  case MYSQL_TYPE_DECIMAL: // char[]
494
632
  case MYSQL_TYPE_NEWDECIMAL: // char[]
495
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
633
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
496
634
  break;
497
635
  case MYSQL_TYPE_STRING: // char[]
498
636
  case MYSQL_TYPE_VAR_STRING: // char[]
@@ -506,9 +644,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
506
644
  case MYSQL_TYPE_GEOMETRY: // char[]
507
645
  default:
508
646
  val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
509
- #ifdef HAVE_RUBY_ENCODING_H
510
647
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
511
- #endif
512
648
  break;
513
649
  }
514
650
  }
@@ -530,16 +666,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
530
666
  unsigned int i = 0;
531
667
  unsigned long * fieldLengths;
532
668
  void * ptr;
533
- #ifdef HAVE_RUBY_ENCODING_H
534
669
  rb_encoding *default_internal_enc;
535
670
  rb_encoding *conn_enc;
536
- #endif
537
671
  GET_RESULT(self);
538
672
 
539
- #ifdef HAVE_RUBY_ENCODING_H
540
673
  default_internal_enc = rb_default_internal_encoding();
541
674
  conn_enc = rb_to_encoding(wrapper->encoding);
542
- #endif
543
675
 
544
676
  ptr = wrapper->result;
545
677
  row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
@@ -569,9 +701,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
569
701
  val = Qnil;
570
702
  } else {
571
703
  val = rb_str_new(row[i], fieldLengths[i]);
572
- #ifdef HAVE_RUBY_ENCODING_H
573
704
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
574
- #endif
575
705
  }
576
706
  } else {
577
707
  switch(type) {
@@ -602,9 +732,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
602
732
  if (fields[i].decimals == 0) {
603
733
  val = rb_cstr2inum(row[i], 10);
604
734
  } else if (strtod(row[i], NULL) == 0.000000){
605
- val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
735
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
606
736
  }else{
607
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
737
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
608
738
  }
609
739
  break;
610
740
  case MYSQL_TYPE_FLOAT: /* FLOAT field */
@@ -722,9 +852,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
722
852
  case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
723
853
  default:
724
854
  val = rb_str_new(row[i], fieldLengths[i]);
725
- #ifdef HAVE_RUBY_ENCODING_H
726
855
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
727
- #endif
728
856
  break;
729
857
  }
730
858
  }
@@ -751,7 +879,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
751
879
 
752
880
  GET_RESULT(self);
753
881
 
754
- defaults = rb_iv_get(self, "@query_options");
882
+ defaults = rb_ivar_get(self, intern_query_options);
755
883
  Check_Type(defaults, T_HASH);
756
884
  if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
757
885
  symbolizeKeys = 1;
@@ -771,6 +899,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
771
899
  return wrapper->fields;
772
900
  }
773
901
 
902
+ static VALUE rb_mysql_result_fetch_field_types(VALUE self) {
903
+ unsigned int i = 0;
904
+
905
+ GET_RESULT(self);
906
+
907
+ if (wrapper->fieldTypes == Qnil) {
908
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
909
+ wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
910
+ }
911
+
912
+ if ((my_ulonglong)RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
913
+ for (i=0; i<wrapper->numberOfFields; i++) {
914
+ rb_mysql_result_fetch_field_type(self, i);
915
+ }
916
+ }
917
+
918
+ return wrapper->fieldTypes;
919
+ }
920
+
774
921
  static VALUE rb_mysql_result_each_(VALUE self,
775
922
  VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
776
923
  const result_each_args *args)
@@ -796,7 +943,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
796
943
  row = fetch_row_func(self, fields, args);
797
944
  if (row != Qnil) {
798
945
  wrapper->numberOfRows++;
799
- if (args->block_given != Qnil) {
946
+ if (args->block_given) {
800
947
  rb_yield(row);
801
948
  }
802
949
  }
@@ -846,7 +993,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
846
993
  return Qnil;
847
994
  }
848
995
 
849
- if (args->block_given != Qnil) {
996
+ if (args->block_given) {
850
997
  rb_yield(row);
851
998
  }
852
999
  }
@@ -864,7 +1011,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
864
1011
 
865
1012
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
866
1013
  result_each_args args;
867
- VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
1014
+ VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
868
1015
  ID db_timezone, app_timezone, dbTz, appTz;
869
1016
  int symbolizeKeys, asArray, castBool, cacheRows, cast;
870
1017
 
@@ -874,9 +1021,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
874
1021
  rb_raise(cMysql2Error, "Statement handle already closed");
875
1022
  }
876
1023
 
877
- defaults = rb_iv_get(self, "@query_options");
1024
+ defaults = rb_ivar_get(self, intern_query_options);
878
1025
  Check_Type(defaults, T_HASH);
879
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
1026
+
1027
+ // A block can be passed to this method, but since we don't call the block directly from C,
1028
+ // we don't need to capture it into a variable here with the "&" scan arg.
1029
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
880
1030
  opts = rb_funcall(defaults, intern_merge, 1, opts);
881
1031
  } else {
882
1032
  opts = defaults;
@@ -942,7 +1092,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
942
1092
  args.cast = cast;
943
1093
  args.db_timezone = db_timezone;
944
1094
  args.app_timezone = app_timezone;
945
- args.block_given = block;
1095
+ args.block_given = rb_block_given_p();
946
1096
 
947
1097
  if (wrapper->stmt_wrapper) {
948
1098
  fetch_row_func = rb_mysql_result_fetch_row_stmt;
@@ -986,6 +1136,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
986
1136
  wrapper->resultFreed = 0;
987
1137
  wrapper->result = r;
988
1138
  wrapper->fields = Qnil;
1139
+ wrapper->fieldTypes = Qnil;
989
1140
  wrapper->rows = Qnil;
990
1141
  wrapper->encoding = encoding;
991
1142
  wrapper->streamingComplete = 0;
@@ -1007,7 +1158,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1007
1158
  }
1008
1159
 
1009
1160
  rb_obj_call_init(obj, 0, NULL);
1010
- rb_iv_set(obj, "@query_options", options);
1161
+ rb_ivar_set(obj, intern_query_options, options);
1011
1162
 
1012
1163
  /* Options that cannot be changed in results.each(...) { |row| }
1013
1164
  * should be processed here. */
@@ -1017,13 +1168,18 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1017
1168
  }
1018
1169
 
1019
1170
  void init_mysql2_result() {
1020
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1021
1171
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1172
+ rb_global_variable(&cDate);
1022
1173
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1174
+ rb_global_variable(&cDateTime);
1023
1175
 
1024
1176
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1177
+ rb_undef_alloc_func(cMysql2Result);
1178
+ rb_global_variable(&cMysql2Result);
1179
+
1025
1180
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
1026
1181
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
1182
+ rb_define_method(cMysql2Result, "field_types", rb_mysql_result_fetch_field_types, 0);
1027
1183
  rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
1028
1184
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
1029
1185
  rb_define_alias(cMysql2Result, "size", "count");
@@ -1036,6 +1192,8 @@ void init_mysql2_result() {
1036
1192
  intern_local_offset = rb_intern("local_offset");
1037
1193
  intern_civil = rb_intern("civil");
1038
1194
  intern_new_offset = rb_intern("new_offset");
1195
+ intern_BigDecimal = rb_intern("BigDecimal");
1196
+ intern_query_options = rb_intern("@query_options");
1039
1197
 
1040
1198
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1041
1199
  sym_as = ID2SYM(rb_intern("as"));
@@ -1058,7 +1216,5 @@ void init_mysql2_result() {
1058
1216
  opt_time_month = INT2NUM(1);
1059
1217
  opt_utc_offset = INT2NUM(0);
1060
1218
 
1061
- #ifdef HAVE_RUBY_ENCODING_H
1062
1219
  binaryEncoding = rb_enc_find("binary");
1063
- #endif
1064
1220
  }
data/ext/mysql2/result.h CHANGED
@@ -1,12 +1,12 @@
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);
7
6
 
8
7
  typedef struct {
9
8
  VALUE fields;
9
+ VALUE fieldTypes;
10
10
  VALUE rows;
11
11
  VALUE client;
12
12
  VALUE encoding;
@@ -22,8 +22,8 @@ typedef struct {
22
22
  mysql_client_wrapper *client_wrapper;
23
23
  /* statement result bind buffers */
24
24
  MYSQL_BIND *result_buffers;
25
- bool *is_null;
26
- bool *error;
25
+ my_bool *is_null;
26
+ my_bool *error;
27
27
  unsigned long *length;
28
28
  } mysql2_result_wrapper;
29
29