mysql2 0.4.2 → 0.5.5

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 (52) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +231 -86
  3. data/ext/mysql2/client.c +527 -128
  4. data/ext/mysql2/client.h +11 -52
  5. data/ext/mysql2/extconf.rb +100 -21
  6. data/ext/mysql2/mysql2_ext.c +8 -2
  7. data/ext/mysql2/mysql2_ext.h +21 -8
  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 +333 -109
  11. data/ext/mysql2/result.h +1 -0
  12. data/ext/mysql2/statement.c +247 -90
  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 +71 -31
  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 +19 -15
  22. data/support/3A79BD29.asc +49 -0
  23. data/support/5072E1F5.asc +432 -0
  24. data/support/C74CD1D8.asc +104 -0
  25. data/support/mysql_enc_to_ruby.rb +8 -3
  26. data/support/ruby_enc_to_mysql.rb +7 -5
  27. metadata +19 -61
  28. data/examples/eventmachine.rb +0 -21
  29. data/examples/threaded.rb +0 -18
  30. data/spec/configuration.yml.example +0 -17
  31. data/spec/em/em_spec.rb +0 -135
  32. data/spec/my.cnf.example +0 -9
  33. data/spec/mysql2/client_spec.rb +0 -939
  34. data/spec/mysql2/error_spec.rb +0 -84
  35. data/spec/mysql2/result_spec.rb +0 -510
  36. data/spec/mysql2/statement_spec.rb +0 -684
  37. data/spec/rcov.opts +0 -3
  38. data/spec/spec_helper.rb +0 -94
  39. data/spec/ssl/ca-cert.pem +0 -17
  40. data/spec/ssl/ca-key.pem +0 -27
  41. data/spec/ssl/ca.cnf +0 -22
  42. data/spec/ssl/cert.cnf +0 -22
  43. data/spec/ssl/client-cert.pem +0 -17
  44. data/spec/ssl/client-key.pem +0 -27
  45. data/spec/ssl/client-req.pem +0 -15
  46. data/spec/ssl/gen_certs.sh +0 -48
  47. data/spec/ssl/pkcs8-client-key.pem +0 -28
  48. data/spec/ssl/pkcs8-server-key.pem +0 -28
  49. data/spec/ssl/server-cert.pem +0 -17
  50. data/spec/ssl/server-key.pem +0 -27
  51. data/spec/ssl/server-req.pem +0 -15
  52. data/spec/test_data +0 -1
data/ext/mysql2/result.c CHANGED
@@ -1,56 +1,43 @@
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
32
+ #endif
33
+
34
+ #ifndef NEW_TYPEDDATA_WRAPPER
35
+ #define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
49
36
  #endif
50
37
 
51
38
  #define GET_RESULT(self) \
52
39
  mysql2_result_wrapper *wrapper; \
53
- Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
40
+ TypedData_Get_Struct(self, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
54
41
 
55
42
  typedef struct {
56
43
  int symbolizeKeys;
@@ -61,27 +48,28 @@ typedef struct {
61
48
  int streaming;
62
49
  ID db_timezone;
63
50
  ID app_timezone;
64
- VALUE block_given;
51
+ int block_given; /* boolean */
65
52
  } result_each_args;
66
53
 
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
54
  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;
55
+ static VALUE cMysql2Result, cDateTime, cDate;
56
+ static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
57
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
58
+ intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
59
+ intern_query_options;
60
+ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
61
+ sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
62
+ sym_cache_rows, sym_cast, sym_stream, sym_name;
75
63
 
76
64
  /* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
77
65
  static void rb_mysql_result_mark(void * wrapper) {
78
66
  mysql2_result_wrapper * w = wrapper;
79
67
  if (w) {
80
- rb_gc_mark(w->fields);
81
- rb_gc_mark(w->rows);
82
- rb_gc_mark(w->encoding);
83
- rb_gc_mark(w->client);
84
- 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);
85
73
  }
86
74
  }
87
75
 
@@ -104,6 +92,10 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
104
92
  wrapper->stmt_wrapper->stmt->bind_result_done = 0;
105
93
  }
106
94
 
95
+ if (wrapper->statement != Qnil) {
96
+ decr_mysql2_stmt(wrapper->stmt_wrapper);
97
+ }
98
+
107
99
  if (wrapper->result_buffers) {
108
100
  unsigned int i;
109
101
  for (i = 0; i < wrapper->numberOfFields; i++) {
@@ -136,11 +128,55 @@ static void rb_mysql_result_free(void *ptr) {
136
128
  decr_mysql2_client(wrapper->client_wrapper);
137
129
  }
138
130
 
139
- if (wrapper->statement != Qnil) {
140
- decr_mysql2_stmt(wrapper->stmt_wrapper);
131
+ xfree(wrapper);
132
+ }
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);
141
155
  }
156
+ }
157
+ #endif
142
158
 
143
- xfree(wrapper);
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
+
176
+ static VALUE rb_mysql_result_free_(VALUE self) {
177
+ GET_RESULT(self);
178
+ rb_mysql_result_free_result(wrapper);
179
+ return Qnil;
144
180
  }
145
181
 
146
182
  /*
@@ -173,28 +209,25 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
173
209
  rb_field = rb_ary_entry(wrapper->fields, idx);
174
210
  if (rb_field == Qnil) {
175
211
  MYSQL_FIELD *field = NULL;
176
- #ifdef HAVE_RUBY_ENCODING_H
177
212
  rb_encoding *default_internal_enc = rb_default_internal_encoding();
178
213
  rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
179
- #endif
180
214
 
181
215
  field = mysql_fetch_field_direct(wrapper->result, idx);
182
216
  if (symbolize_keys) {
183
- #ifdef HAVE_RB_INTERN3
184
217
  rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
185
218
  rb_field = ID2SYM(rb_field);
186
- #else
187
- VALUE colStr;
188
- colStr = rb_str_new(field->name, field->name_length);
189
- rb_field = ID2SYM(rb_to_id(colStr));
190
- #endif
191
219
  } else {
192
- rb_field = rb_str_new(field->name, field->name_length);
193
- #ifdef HAVE_RUBY_ENCODING_H
194
- rb_enc_associate(rb_field, conn_enc);
195
- 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) {
196
228
  rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
197
229
  }
230
+ rb_obj_freeze(rb_field);
198
231
  #endif
199
232
  }
200
233
  rb_ary_store(wrapper->fields, idx, rb_field);
@@ -203,10 +236,171 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbo
203
236
  return rb_field;
204
237
  }
205
238
 
206
- #ifdef HAVE_RUBY_ENCODING_H
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
+
207
401
  static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
208
402
  /* if binary flag is set, respect its wishes */
209
- if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
403
+ if (field.flags & BINARY_FLAG && field.charsetnr == MYSQL2_BINARY_CHARSET) {
210
404
  rb_enc_associate(val, binaryEncoding);
211
405
  } else if (!field.charsetnr) {
212
406
  /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
@@ -216,7 +410,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
216
410
  const char *enc_name;
217
411
  int enc_index;
218
412
 
219
- enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
413
+ enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
414
+
220
415
  if (enc_name != NULL) {
221
416
  /* use the field encoding we were able to match */
222
417
  enc_index = rb_enc_find_index(enc_name);
@@ -232,7 +427,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
232
427
  }
233
428
  return val;
234
429
  }
235
- #endif
236
430
 
237
431
  /* Interpret microseconds digits left-aligned in fixed-width field.
238
432
  * e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
@@ -272,12 +466,12 @@ static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields
272
466
  wrapper->result_buffers[i].buffer_length = sizeof(signed char);
273
467
  break;
274
468
  case MYSQL_TYPE_SHORT: // short int
469
+ case MYSQL_TYPE_YEAR: // short int
275
470
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
276
471
  wrapper->result_buffers[i].buffer_length = sizeof(short int);
277
472
  break;
278
473
  case MYSQL_TYPE_INT24: // int
279
474
  case MYSQL_TYPE_LONG: // int
280
- case MYSQL_TYPE_YEAR: // int
281
475
  wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
282
476
  wrapper->result_buffers[i].buffer_length = sizeof(int);
283
477
  break;
@@ -329,26 +523,22 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
329
523
  VALUE rowVal;
330
524
  unsigned int i = 0;
331
525
 
332
- #ifdef HAVE_RUBY_ENCODING_H
333
526
  rb_encoding *default_internal_enc;
334
527
  rb_encoding *conn_enc;
335
- #endif
336
528
  GET_RESULT(self);
337
529
 
338
- #ifdef HAVE_RUBY_ENCODING_H
339
530
  default_internal_enc = rb_default_internal_encoding();
340
531
  conn_enc = rb_to_encoding(wrapper->encoding);
341
- #endif
342
532
 
533
+ if (wrapper->fields == Qnil) {
534
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
535
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
536
+ }
343
537
  if (args->asArray) {
344
538
  rowVal = rb_ary_new2(wrapper->numberOfFields);
345
539
  } else {
346
540
  rowVal = rb_hash_new();
347
541
  }
348
- if (wrapper->fields == Qnil) {
349
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
350
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
351
- }
352
542
 
353
543
  if (wrapper->result_buffers == NULL) {
354
544
  rb_mysql_result_alloc_result_buffers(self, fields);
@@ -399,7 +589,15 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
399
589
  val = INT2NUM(*((signed char*)result_buffer->buffer));
400
590
  }
401
591
  break;
592
+ case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
593
+ if (args->castBool && fields[i].length == 1) {
594
+ val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
595
+ }else{
596
+ val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
597
+ }
598
+ break;
402
599
  case MYSQL_TYPE_SHORT: // short int
600
+ case MYSQL_TYPE_YEAR: // short int
403
601
  if (result_buffer->is_unsigned) {
404
602
  val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
405
603
  } else {
@@ -408,7 +606,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
408
606
  break;
409
607
  case MYSQL_TYPE_INT24: // int
410
608
  case MYSQL_TYPE_LONG: // int
411
- case MYSQL_TYPE_YEAR: // int
412
609
  if (result_buffer->is_unsigned) {
413
610
  val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
414
611
  } else {
@@ -479,7 +676,7 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
479
676
  }
480
677
  case MYSQL_TYPE_DECIMAL: // char[]
481
678
  case MYSQL_TYPE_NEWDECIMAL: // char[]
482
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
679
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
483
680
  break;
484
681
  case MYSQL_TYPE_STRING: // char[]
485
682
  case MYSQL_TYPE_VAR_STRING: // char[]
@@ -488,15 +685,12 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
488
685
  case MYSQL_TYPE_BLOB: // char[]
489
686
  case MYSQL_TYPE_MEDIUM_BLOB: // char[]
490
687
  case MYSQL_TYPE_LONG_BLOB: // char[]
491
- case MYSQL_TYPE_BIT: // char[]
492
688
  case MYSQL_TYPE_SET: // char[]
493
689
  case MYSQL_TYPE_ENUM: // char[]
494
690
  case MYSQL_TYPE_GEOMETRY: // char[]
495
691
  default:
496
692
  val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
497
- #ifdef HAVE_RUBY_ENCODING_H
498
693
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
499
- #endif
500
694
  break;
501
695
  }
502
696
  }
@@ -511,7 +705,6 @@ static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, co
511
705
  return rowVal;
512
706
  }
513
707
 
514
-
515
708
  static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
516
709
  {
517
710
  VALUE rowVal;
@@ -519,16 +712,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
519
712
  unsigned int i = 0;
520
713
  unsigned long * fieldLengths;
521
714
  void * ptr;
522
- #ifdef HAVE_RUBY_ENCODING_H
523
715
  rb_encoding *default_internal_enc;
524
716
  rb_encoding *conn_enc;
525
- #endif
526
717
  GET_RESULT(self);
527
718
 
528
- #ifdef HAVE_RUBY_ENCODING_H
529
719
  default_internal_enc = rb_default_internal_encoding();
530
720
  conn_enc = rb_to_encoding(wrapper->encoding);
531
- #endif
532
721
 
533
722
  ptr = wrapper->result;
534
723
  row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
@@ -536,16 +725,16 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
536
725
  return Qnil;
537
726
  }
538
727
 
728
+ if (wrapper->fields == Qnil) {
729
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
730
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
731
+ }
539
732
  if (args->asArray) {
540
733
  rowVal = rb_ary_new2(wrapper->numberOfFields);
541
734
  } else {
542
735
  rowVal = rb_hash_new();
543
736
  }
544
737
  fieldLengths = mysql_fetch_lengths(wrapper->result);
545
- if (wrapper->fields == Qnil) {
546
- wrapper->numberOfFields = mysql_num_fields(wrapper->result);
547
- wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
548
- }
549
738
 
550
739
  for (i = 0; i < wrapper->numberOfFields; i++) {
551
740
  VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
@@ -558,9 +747,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
558
747
  val = Qnil;
559
748
  } else {
560
749
  val = rb_str_new(row[i], fieldLengths[i]);
561
- #ifdef HAVE_RUBY_ENCODING_H
562
750
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
563
- #endif
564
751
  }
565
752
  } else {
566
753
  switch(type) {
@@ -591,9 +778,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
591
778
  if (fields[i].decimals == 0) {
592
779
  val = rb_cstr2inum(row[i], 10);
593
780
  } else if (strtod(row[i], NULL) == 0.000000){
594
- val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
781
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
595
782
  }else{
596
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
783
+ val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
597
784
  }
598
785
  break;
599
786
  case MYSQL_TYPE_FLOAT: /* FLOAT field */
@@ -711,9 +898,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const r
711
898
  case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
712
899
  default:
713
900
  val = rb_str_new(row[i], fieldLengths[i]);
714
- #ifdef HAVE_RUBY_ENCODING_H
715
901
  val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
716
- #endif
717
902
  break;
718
903
  }
719
904
  }
@@ -740,7 +925,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
740
925
 
741
926
  GET_RESULT(self);
742
927
 
743
- defaults = rb_iv_get(self, "@query_options");
928
+ defaults = rb_ivar_get(self, intern_query_options);
744
929
  Check_Type(defaults, T_HASH);
745
930
  if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
746
931
  symbolizeKeys = 1;
@@ -760,6 +945,25 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
760
945
  return wrapper->fields;
761
946
  }
762
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
+
763
967
  static VALUE rb_mysql_result_each_(VALUE self,
764
968
  VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
765
969
  const result_each_args *args)
@@ -785,7 +989,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
785
989
  row = fetch_row_func(self, fields, args);
786
990
  if (row != Qnil) {
787
991
  wrapper->numberOfRows++;
788
- if (args->block_given != Qnil) {
992
+ if (args->block_given) {
789
993
  rb_yield(row);
790
994
  }
791
995
  }
@@ -829,15 +1033,17 @@ static VALUE rb_mysql_result_each_(VALUE self,
829
1033
 
830
1034
  if (row == Qnil) {
831
1035
  /* we don't need the mysql C dataset around anymore, peace it */
832
- rb_mysql_result_free_result(wrapper);
1036
+ if (args->cacheRows) {
1037
+ rb_mysql_result_free_result(wrapper);
1038
+ }
833
1039
  return Qnil;
834
1040
  }
835
1041
 
836
- if (args->block_given != Qnil) {
1042
+ if (args->block_given) {
837
1043
  rb_yield(row);
838
1044
  }
839
1045
  }
840
- if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
1046
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
841
1047
  /* we don't need the mysql C dataset around anymore, peace it */
842
1048
  rb_mysql_result_free_result(wrapper);
843
1049
  }
@@ -851,7 +1057,7 @@ static VALUE rb_mysql_result_each_(VALUE self,
851
1057
 
852
1058
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
853
1059
  result_each_args args;
854
- 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);
855
1061
  ID db_timezone, app_timezone, dbTz, appTz;
856
1062
  int symbolizeKeys, asArray, castBool, cacheRows, cast;
857
1063
 
@@ -861,9 +1067,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
861
1067
  rb_raise(cMysql2Error, "Statement handle already closed");
862
1068
  }
863
1069
 
864
- defaults = rb_iv_get(self, "@query_options");
1070
+ defaults = rb_ivar_get(self, intern_query_options);
865
1071
  Check_Type(defaults, T_HASH);
866
- 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) {
867
1076
  opts = rb_funcall(defaults, intern_merge, 1, opts);
868
1077
  } else {
869
1078
  opts = defaults;
@@ -881,6 +1090,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
881
1090
 
882
1091
  if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
883
1092
  rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
1093
+ cacheRows = 1;
884
1094
  }
885
1095
 
886
1096
  if (wrapper->stmt_wrapper && !cast) {
@@ -908,12 +1118,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
908
1118
  app_timezone = Qnil;
909
1119
  }
910
1120
 
911
- if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
1121
+ if (wrapper->rows == Qnil && !wrapper->is_streaming) {
912
1122
  wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
913
- if (wrapper->numberOfRows == 0) {
914
- wrapper->rows = rb_ary_new();
915
- return wrapper->rows;
1123
+ wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
1124
+ } else if (wrapper->rows && !cacheRows) {
1125
+ if (wrapper->resultFreed) {
1126
+ rb_raise(cMysql2Error, "Result set has already been freed");
916
1127
  }
1128
+ mysql_data_seek(wrapper->result, 0);
1129
+ wrapper->lastRowProcessed = 0;
917
1130
  wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
918
1131
  }
919
1132
 
@@ -925,7 +1138,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
925
1138
  args.cast = cast;
926
1139
  args.db_timezone = db_timezone;
927
1140
  args.app_timezone = app_timezone;
928
- args.block_given = block;
1141
+ args.block_given = rb_block_given_p();
929
1142
 
930
1143
  if (wrapper->stmt_wrapper) {
931
1144
  fetch_row_func = rb_mysql_result_fetch_row_stmt;
@@ -962,13 +1175,18 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
962
1175
  VALUE obj;
963
1176
  mysql2_result_wrapper * wrapper;
964
1177
 
1178
+ #ifdef NEW_TYPEDDATA_WRAPPER
1179
+ obj = TypedData_Make_Struct(cMysql2Result, mysql2_result_wrapper, &rb_mysql_result_type, wrapper);
1180
+ #else
965
1181
  obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
1182
+ #endif
966
1183
  wrapper->numberOfFields = 0;
967
1184
  wrapper->numberOfRows = 0;
968
1185
  wrapper->lastRowProcessed = 0;
969
1186
  wrapper->resultFreed = 0;
970
1187
  wrapper->result = r;
971
1188
  wrapper->fields = Qnil;
1189
+ wrapper->fieldTypes = Qnil;
972
1190
  wrapper->rows = Qnil;
973
1191
  wrapper->encoding = encoding;
974
1192
  wrapper->streamingComplete = 0;
@@ -990,7 +1208,7 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
990
1208
  }
991
1209
 
992
1210
  rb_obj_call_init(obj, 0, NULL);
993
- rb_iv_set(obj, "@query_options", options);
1211
+ rb_ivar_set(obj, intern_query_options, options);
994
1212
 
995
1213
  /* Options that cannot be changed in results.each(...) { |row| }
996
1214
  * should be processed here. */
@@ -1000,13 +1218,19 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
1000
1218
  }
1001
1219
 
1002
1220
  void init_mysql2_result() {
1003
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1004
1221
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
1222
+ rb_global_variable(&cDate);
1005
1223
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
1224
+ rb_global_variable(&cDateTime);
1006
1225
 
1007
1226
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
1227
+ rb_undef_alloc_func(cMysql2Result);
1228
+ rb_global_variable(&cMysql2Result);
1229
+
1008
1230
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
1009
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);
1233
+ rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
1010
1234
  rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
1011
1235
  rb_define_alias(cMysql2Result, "size", "count");
1012
1236
 
@@ -1018,6 +1242,8 @@ void init_mysql2_result() {
1018
1242
  intern_local_offset = rb_intern("local_offset");
1019
1243
  intern_civil = rb_intern("civil");
1020
1244
  intern_new_offset = rb_intern("new_offset");
1245
+ intern_BigDecimal = rb_intern("BigDecimal");
1246
+ intern_query_options = rb_intern("@query_options");
1021
1247
 
1022
1248
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1023
1249
  sym_as = ID2SYM(rb_intern("as"));
@@ -1040,7 +1266,5 @@ void init_mysql2_result() {
1040
1266
  opt_time_month = INT2NUM(1);
1041
1267
  opt_utc_offset = INT2NUM(0);
1042
1268
 
1043
- #ifdef HAVE_RUBY_ENCODING_H
1044
1269
  binaryEncoding = rb_enc_find("binary");
1045
- #endif
1046
1270
  }