mysql2 0.4.2 → 0.5.5

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