mysql2 0.3.18 → 0.4.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/LICENSE +21 -0
- data/README.md +63 -12
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +170 -175
- data/ext/mysql2/client.h +21 -1
- data/ext/mysql2/extconf.rb +95 -35
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +5 -6
- data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
- data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
- data/ext/mysql2/result.c +494 -132
- data/ext/mysql2/result.h +12 -6
- data/ext/mysql2/statement.c +494 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +68 -22
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +5 -6
- data/lib/mysql2/error.rb +18 -27
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/statement.rb +17 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +38 -18
- data/spec/em/em_spec.rb +21 -21
- data/spec/mysql2/client_spec.rb +393 -351
- data/spec/mysql2/error_spec.rb +37 -36
- data/spec/mysql2/result_spec.rb +213 -208
- data/spec/mysql2/statement_spec.rb +684 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/support/mysql_enc_to_ruby.rb +7 -8
- data/support/ruby_enc_to_mysql.rb +1 -1
- metadata +41 -46
data/ext/mysql2/result.c
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
-
#include <stdint.h>
|
4
|
-
|
5
3
|
#include "mysql_enc_to_ruby.h"
|
6
4
|
|
7
5
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -50,8 +48,24 @@ static rb_encoding *binaryEncoding;
|
|
50
48
|
#define MYSQL2_MIN_TIME 62171150401ULL
|
51
49
|
#endif
|
52
50
|
|
51
|
+
#define GET_RESULT(self) \
|
52
|
+
mysql2_result_wrapper *wrapper; \
|
53
|
+
Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
|
54
|
+
|
55
|
+
typedef struct {
|
56
|
+
int symbolizeKeys;
|
57
|
+
int asArray;
|
58
|
+
int castBool;
|
59
|
+
int cacheRows;
|
60
|
+
int cast;
|
61
|
+
int streaming;
|
62
|
+
ID db_timezone;
|
63
|
+
ID app_timezone;
|
64
|
+
VALUE block_given;
|
65
|
+
} result_each_args;
|
66
|
+
|
67
|
+
VALUE cBigDecimal, cDateTime, cDate;
|
53
68
|
static VALUE cMysql2Result;
|
54
|
-
static VALUE cBigDecimal, cDate, cDateTime;
|
55
69
|
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
56
70
|
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
57
71
|
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
|
@@ -59,6 +73,7 @@ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_a
|
|
59
73
|
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
|
60
74
|
static ID intern_merge;
|
61
75
|
|
76
|
+
/* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
|
62
77
|
static void rb_mysql_result_mark(void * wrapper) {
|
63
78
|
mysql2_result_wrapper * w = wrapper;
|
64
79
|
if (w) {
|
@@ -66,13 +81,46 @@ static void rb_mysql_result_mark(void * wrapper) {
|
|
66
81
|
rb_gc_mark(w->rows);
|
67
82
|
rb_gc_mark(w->encoding);
|
68
83
|
rb_gc_mark(w->client);
|
84
|
+
rb_gc_mark(w->statement);
|
69
85
|
}
|
70
86
|
}
|
71
87
|
|
72
88
|
/* this may be called manually or during GC */
|
73
89
|
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
74
|
-
if (wrapper
|
90
|
+
if (!wrapper) return;
|
91
|
+
|
92
|
+
if (wrapper->resultFreed != 1) {
|
93
|
+
if (wrapper->stmt_wrapper) {
|
94
|
+
if (!wrapper->stmt_wrapper->closed) {
|
95
|
+
mysql_stmt_free_result(wrapper->stmt_wrapper->stmt);
|
96
|
+
|
97
|
+
/* MySQL BUG? If the statement handle was previously used, and so
|
98
|
+
* mysql_stmt_bind_result was called, and if that result set and bind buffers were freed,
|
99
|
+
* MySQL still thinks the result set buffer is available and will prefetch the
|
100
|
+
* first result in mysql_stmt_execute. This will corrupt or crash the program.
|
101
|
+
* By setting bind_result_done back to 0, we make MySQL think that a result set
|
102
|
+
* has never been bound to this statement handle before to prevent the prefetch.
|
103
|
+
*/
|
104
|
+
wrapper->stmt_wrapper->stmt->bind_result_done = 0;
|
105
|
+
}
|
106
|
+
|
107
|
+
if (wrapper->result_buffers) {
|
108
|
+
unsigned int i;
|
109
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
110
|
+
if (wrapper->result_buffers[i].buffer) {
|
111
|
+
xfree(wrapper->result_buffers[i].buffer);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
xfree(wrapper->result_buffers);
|
115
|
+
xfree(wrapper->is_null);
|
116
|
+
xfree(wrapper->error);
|
117
|
+
xfree(wrapper->length);
|
118
|
+
}
|
119
|
+
/* Clue that the next statement execute will need to allocate a new result buffer. */
|
120
|
+
wrapper->result_buffers = NULL;
|
121
|
+
}
|
75
122
|
/* FIXME: this may call flush_use_result, which can hit the socket */
|
123
|
+
/* For prepared statements, wrapper->result is the result metadata */
|
76
124
|
mysql_free_result(wrapper->result);
|
77
125
|
wrapper->resultFreed = 1;
|
78
126
|
}
|
@@ -80,7 +128,7 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
80
128
|
|
81
129
|
/* this is called during GC */
|
82
130
|
static void rb_mysql_result_free(void *ptr) {
|
83
|
-
mysql2_result_wrapper *
|
131
|
+
mysql2_result_wrapper *wrapper = ptr;
|
84
132
|
rb_mysql_result_free_result(wrapper);
|
85
133
|
|
86
134
|
// If the GC gets to client first it will be nil
|
@@ -88,6 +136,10 @@ static void rb_mysql_result_free(void *ptr) {
|
|
88
136
|
decr_mysql2_client(wrapper->client_wrapper);
|
89
137
|
}
|
90
138
|
|
139
|
+
if (wrapper->statement != Qnil) {
|
140
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
141
|
+
}
|
142
|
+
|
91
143
|
xfree(wrapper);
|
92
144
|
}
|
93
145
|
|
@@ -102,10 +154,16 @@ static void *nogvl_fetch_row(void *ptr) {
|
|
102
154
|
return mysql_fetch_row(result);
|
103
155
|
}
|
104
156
|
|
105
|
-
static
|
106
|
-
|
157
|
+
static void *nogvl_stmt_fetch(void *ptr) {
|
158
|
+
MYSQL_STMT *stmt = ptr;
|
159
|
+
uintptr_t r = mysql_stmt_fetch(stmt);
|
160
|
+
|
161
|
+
return (void *)r;
|
162
|
+
}
|
163
|
+
|
164
|
+
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
|
107
165
|
VALUE rb_field;
|
108
|
-
|
166
|
+
GET_RESULT(self);
|
109
167
|
|
110
168
|
if (wrapper->fields == Qnil) {
|
111
169
|
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
@@ -147,7 +205,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
147
205
|
|
148
206
|
#ifdef HAVE_RUBY_ENCODING_H
|
149
207
|
static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
|
150
|
-
/* if binary flag is set, respect
|
208
|
+
/* if binary flag is set, respect its wishes */
|
151
209
|
if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
|
152
210
|
rb_enc_associate(val, binaryEncoding);
|
153
211
|
} else if (!field.charsetnr) {
|
@@ -182,7 +240,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
182
240
|
*/
|
183
241
|
static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
184
242
|
{
|
185
|
-
|
243
|
+
size_t i;
|
186
244
|
for (i = 0; i < (len - 1); i++) {
|
187
245
|
if (msec_char[i] == '\0') {
|
188
246
|
msec_char[i] = '0';
|
@@ -191,9 +249,272 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
191
249
|
return (unsigned int)strtoul(msec_char, NULL, 10);
|
192
250
|
}
|
193
251
|
|
194
|
-
static
|
252
|
+
static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) {
|
253
|
+
unsigned int i;
|
254
|
+
GET_RESULT(self);
|
255
|
+
|
256
|
+
if (wrapper->result_buffers != NULL) return;
|
257
|
+
|
258
|
+
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
259
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
260
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
261
|
+
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
262
|
+
|
263
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
264
|
+
wrapper->result_buffers[i].buffer_type = fields[i].type;
|
265
|
+
|
266
|
+
// mysql type | C type
|
267
|
+
switch(fields[i].type) {
|
268
|
+
case MYSQL_TYPE_NULL: // NULL
|
269
|
+
break;
|
270
|
+
case MYSQL_TYPE_TINY: // signed char
|
271
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(signed char));
|
272
|
+
wrapper->result_buffers[i].buffer_length = sizeof(signed char);
|
273
|
+
break;
|
274
|
+
case MYSQL_TYPE_SHORT: // short int
|
275
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
|
276
|
+
wrapper->result_buffers[i].buffer_length = sizeof(short int);
|
277
|
+
break;
|
278
|
+
case MYSQL_TYPE_INT24: // int
|
279
|
+
case MYSQL_TYPE_LONG: // int
|
280
|
+
case MYSQL_TYPE_YEAR: // int
|
281
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
|
282
|
+
wrapper->result_buffers[i].buffer_length = sizeof(int);
|
283
|
+
break;
|
284
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
285
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(long long int));
|
286
|
+
wrapper->result_buffers[i].buffer_length = sizeof(long long int);
|
287
|
+
break;
|
288
|
+
case MYSQL_TYPE_FLOAT: // float
|
289
|
+
case MYSQL_TYPE_DOUBLE: // double
|
290
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(double));
|
291
|
+
wrapper->result_buffers[i].buffer_length = sizeof(double);
|
292
|
+
break;
|
293
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
294
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
295
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
296
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
297
|
+
case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
|
298
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(MYSQL_TIME));
|
299
|
+
wrapper->result_buffers[i].buffer_length = sizeof(MYSQL_TIME);
|
300
|
+
break;
|
301
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
302
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
303
|
+
case MYSQL_TYPE_STRING: // char[]
|
304
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
305
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
306
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
307
|
+
case MYSQL_TYPE_BLOB: // char[]
|
308
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
309
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
310
|
+
case MYSQL_TYPE_BIT: // char[]
|
311
|
+
case MYSQL_TYPE_SET: // char[]
|
312
|
+
case MYSQL_TYPE_ENUM: // char[]
|
313
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
314
|
+
default:
|
315
|
+
wrapper->result_buffers[i].buffer = xmalloc(fields[i].max_length);
|
316
|
+
wrapper->result_buffers[i].buffer_length = fields[i].max_length;
|
317
|
+
break;
|
318
|
+
}
|
319
|
+
|
320
|
+
wrapper->result_buffers[i].is_null = &wrapper->is_null[i];
|
321
|
+
wrapper->result_buffers[i].length = &wrapper->length[i];
|
322
|
+
wrapper->result_buffers[i].error = &wrapper->error[i];
|
323
|
+
wrapper->result_buffers[i].is_unsigned = ((fields[i].flags & UNSIGNED_FLAG) != 0);
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
328
|
+
{
|
329
|
+
VALUE rowVal;
|
330
|
+
unsigned int i = 0;
|
331
|
+
|
332
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
333
|
+
rb_encoding *default_internal_enc;
|
334
|
+
rb_encoding *conn_enc;
|
335
|
+
#endif
|
336
|
+
GET_RESULT(self);
|
337
|
+
|
338
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
339
|
+
default_internal_enc = rb_default_internal_encoding();
|
340
|
+
conn_enc = rb_to_encoding(wrapper->encoding);
|
341
|
+
#endif
|
342
|
+
|
343
|
+
if (args->asArray) {
|
344
|
+
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
345
|
+
} else {
|
346
|
+
rowVal = rb_hash_new();
|
347
|
+
}
|
348
|
+
if (wrapper->fields == Qnil) {
|
349
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
350
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
351
|
+
}
|
352
|
+
|
353
|
+
if (wrapper->result_buffers == NULL) {
|
354
|
+
rb_mysql_result_alloc_result_buffers(self, fields);
|
355
|
+
}
|
356
|
+
|
357
|
+
if (mysql_stmt_bind_result(wrapper->stmt_wrapper->stmt, wrapper->result_buffers)) {
|
358
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
359
|
+
}
|
360
|
+
|
361
|
+
{
|
362
|
+
switch((uintptr_t)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper->stmt_wrapper->stmt, RUBY_UBF_IO, 0)) {
|
363
|
+
case 0:
|
364
|
+
/* success */
|
365
|
+
break;
|
366
|
+
|
367
|
+
case 1:
|
368
|
+
/* error */
|
369
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
370
|
+
|
371
|
+
case MYSQL_NO_DATA:
|
372
|
+
/* no more row */
|
373
|
+
return Qnil;
|
374
|
+
|
375
|
+
case MYSQL_DATA_TRUNCATED:
|
376
|
+
rb_raise(cMysql2Error, "IMPLBUG: caught MYSQL_DATA_TRUNCATED. should not come here as buffer_length is set to fields[i].max_length.");
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
381
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
382
|
+
VALUE val = Qnil;
|
383
|
+
MYSQL_TIME *ts;
|
384
|
+
|
385
|
+
if (wrapper->is_null[i]) {
|
386
|
+
val = Qnil;
|
387
|
+
} else {
|
388
|
+
const MYSQL_BIND* const result_buffer = &wrapper->result_buffers[i];
|
389
|
+
|
390
|
+
switch(result_buffer->buffer_type) {
|
391
|
+
case MYSQL_TYPE_TINY: // signed char
|
392
|
+
if (args->castBool && fields[i].length == 1) {
|
393
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
394
|
+
break;
|
395
|
+
}
|
396
|
+
if (result_buffer->is_unsigned) {
|
397
|
+
val = UINT2NUM(*((unsigned char*)result_buffer->buffer));
|
398
|
+
} else {
|
399
|
+
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
400
|
+
}
|
401
|
+
break;
|
402
|
+
case MYSQL_TYPE_SHORT: // short int
|
403
|
+
if (result_buffer->is_unsigned) {
|
404
|
+
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
405
|
+
} else {
|
406
|
+
val = INT2NUM(*((short int*)result_buffer->buffer));
|
407
|
+
}
|
408
|
+
break;
|
409
|
+
case MYSQL_TYPE_INT24: // int
|
410
|
+
case MYSQL_TYPE_LONG: // int
|
411
|
+
case MYSQL_TYPE_YEAR: // int
|
412
|
+
if (result_buffer->is_unsigned) {
|
413
|
+
val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
|
414
|
+
} else {
|
415
|
+
val = INT2NUM(*((int*)result_buffer->buffer));
|
416
|
+
}
|
417
|
+
break;
|
418
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
419
|
+
if (result_buffer->is_unsigned) {
|
420
|
+
val = ULL2NUM(*((unsigned long long int*)result_buffer->buffer));
|
421
|
+
} else {
|
422
|
+
val = LL2NUM(*((long long int*)result_buffer->buffer));
|
423
|
+
}
|
424
|
+
break;
|
425
|
+
case MYSQL_TYPE_FLOAT: // float
|
426
|
+
val = rb_float_new((double)(*((float*)result_buffer->buffer)));
|
427
|
+
break;
|
428
|
+
case MYSQL_TYPE_DOUBLE: // double
|
429
|
+
val = rb_float_new((double)(*((double*)result_buffer->buffer)));
|
430
|
+
break;
|
431
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
432
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
433
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
434
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day));
|
435
|
+
break;
|
436
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
437
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
438
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part));
|
439
|
+
if (!NIL_P(args->app_timezone)) {
|
440
|
+
if (args->app_timezone == intern_local) {
|
441
|
+
val = rb_funcall(val, intern_localtime, 0);
|
442
|
+
} else { // utc
|
443
|
+
val = rb_funcall(val, intern_utc, 0);
|
444
|
+
}
|
445
|
+
}
|
446
|
+
break;
|
447
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
448
|
+
case MYSQL_TYPE_TIMESTAMP: { // MYSQL_TIME
|
449
|
+
uint64_t seconds;
|
450
|
+
|
451
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
452
|
+
seconds = (ts->year*31557600ULL) + (ts->month*2592000ULL) + (ts->day*86400ULL) + (ts->hour*3600ULL) + (ts->minute*60ULL) + ts->second;
|
453
|
+
|
454
|
+
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
|
455
|
+
VALUE offset = INT2NUM(0);
|
456
|
+
if (args->db_timezone == intern_local) {
|
457
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
458
|
+
}
|
459
|
+
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), offset);
|
460
|
+
if (!NIL_P(args->app_timezone)) {
|
461
|
+
if (args->app_timezone == intern_local) {
|
462
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
463
|
+
val = rb_funcall(val, intern_new_offset, 1, offset);
|
464
|
+
} else { // utc
|
465
|
+
val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
|
466
|
+
}
|
467
|
+
}
|
468
|
+
} else {
|
469
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(ts->year), UINT2NUM(ts->month), UINT2NUM(ts->day), UINT2NUM(ts->hour), UINT2NUM(ts->minute), UINT2NUM(ts->second), ULONG2NUM(ts->second_part));
|
470
|
+
if (!NIL_P(args->app_timezone)) {
|
471
|
+
if (args->app_timezone == intern_local) {
|
472
|
+
val = rb_funcall(val, intern_localtime, 0);
|
473
|
+
} else { // utc
|
474
|
+
val = rb_funcall(val, intern_utc, 0);
|
475
|
+
}
|
476
|
+
}
|
477
|
+
}
|
478
|
+
break;
|
479
|
+
}
|
480
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
481
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
482
|
+
val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
|
483
|
+
break;
|
484
|
+
case MYSQL_TYPE_STRING: // char[]
|
485
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
486
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
487
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
488
|
+
case MYSQL_TYPE_BLOB: // char[]
|
489
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
490
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
491
|
+
case MYSQL_TYPE_BIT: // char[]
|
492
|
+
case MYSQL_TYPE_SET: // char[]
|
493
|
+
case MYSQL_TYPE_ENUM: // char[]
|
494
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
495
|
+
default:
|
496
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
497
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
498
|
+
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
499
|
+
#endif
|
500
|
+
break;
|
501
|
+
}
|
502
|
+
}
|
503
|
+
|
504
|
+
if (args->asArray) {
|
505
|
+
rb_ary_push(rowVal, val);
|
506
|
+
} else {
|
507
|
+
rb_hash_aset(rowVal, field, val);
|
508
|
+
}
|
509
|
+
}
|
510
|
+
|
511
|
+
return rowVal;
|
512
|
+
}
|
513
|
+
|
514
|
+
|
515
|
+
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
516
|
+
{
|
195
517
|
VALUE rowVal;
|
196
|
-
mysql2_result_wrapper * wrapper;
|
197
518
|
MYSQL_ROW row;
|
198
519
|
unsigned int i = 0;
|
199
520
|
unsigned long * fieldLengths;
|
@@ -202,7 +523,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
202
523
|
rb_encoding *default_internal_enc;
|
203
524
|
rb_encoding *conn_enc;
|
204
525
|
#endif
|
205
|
-
|
526
|
+
GET_RESULT(self);
|
206
527
|
|
207
528
|
#ifdef HAVE_RUBY_ENCODING_H
|
208
529
|
default_internal_enc = rb_default_internal_encoding();
|
@@ -215,7 +536,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
215
536
|
return Qnil;
|
216
537
|
}
|
217
538
|
|
218
|
-
if (asArray) {
|
539
|
+
if (args->asArray) {
|
219
540
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
220
541
|
} else {
|
221
542
|
rowVal = rb_hash_new();
|
@@ -227,12 +548,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
227
548
|
}
|
228
549
|
|
229
550
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
230
|
-
VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
551
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
231
552
|
if (row[i]) {
|
232
553
|
VALUE val = Qnil;
|
233
554
|
enum enum_field_types type = fields[i].type;
|
234
555
|
|
235
|
-
if (!cast) {
|
556
|
+
if (!args->cast) {
|
236
557
|
if (type == MYSQL_TYPE_NULL) {
|
237
558
|
val = Qnil;
|
238
559
|
} else {
|
@@ -247,14 +568,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
247
568
|
val = Qnil;
|
248
569
|
break;
|
249
570
|
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
250
|
-
if (castBool && fields[i].length == 1) {
|
571
|
+
if (args->castBool && fields[i].length == 1) {
|
251
572
|
val = *row[i] == 1 ? Qtrue : Qfalse;
|
252
573
|
}else{
|
253
574
|
val = rb_str_new(row[i], fieldLengths[i]);
|
254
575
|
}
|
255
576
|
break;
|
256
577
|
case MYSQL_TYPE_TINY: /* TINYINT field */
|
257
|
-
if (castBool && fields[i].length == 1) {
|
578
|
+
if (args->castBool && fields[i].length == 1) {
|
258
579
|
val = *row[i] != '0' ? Qtrue : Qfalse;
|
259
580
|
break;
|
260
581
|
}
|
@@ -297,9 +618,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
297
618
|
break;
|
298
619
|
}
|
299
620
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
300
|
-
val = rb_funcall(rb_cTime, db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
301
|
-
if (!NIL_P(app_timezone)) {
|
302
|
-
if (app_timezone == intern_local) {
|
621
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
622
|
+
if (!NIL_P(args->app_timezone)) {
|
623
|
+
if (args->app_timezone == intern_local) {
|
303
624
|
val = rb_funcall(val, intern_localtime, 0);
|
304
625
|
} else { /* utc */
|
305
626
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -330,12 +651,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
330
651
|
} else {
|
331
652
|
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
|
332
653
|
VALUE offset = INT2NUM(0);
|
333
|
-
if (db_timezone == intern_local) {
|
654
|
+
if (args->db_timezone == intern_local) {
|
334
655
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
335
656
|
}
|
336
657
|
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), offset);
|
337
|
-
if (!NIL_P(app_timezone)) {
|
338
|
-
if (app_timezone == intern_local) {
|
658
|
+
if (!NIL_P(args->app_timezone)) {
|
659
|
+
if (args->app_timezone == intern_local) {
|
339
660
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
340
661
|
val = rb_funcall(val, intern_new_offset, 1, offset);
|
341
662
|
} else { /* utc */
|
@@ -344,9 +665,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
344
665
|
}
|
345
666
|
} else {
|
346
667
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
347
|
-
val = rb_funcall(rb_cTime, db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
348
|
-
if (!NIL_P(app_timezone)) {
|
349
|
-
if (app_timezone == intern_local) {
|
668
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
669
|
+
if (!NIL_P(args->app_timezone)) {
|
670
|
+
if (args->app_timezone == intern_local) {
|
350
671
|
val = rb_funcall(val, intern_localtime, 0);
|
351
672
|
} else { /* utc */
|
352
673
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -396,13 +717,13 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
396
717
|
break;
|
397
718
|
}
|
398
719
|
}
|
399
|
-
if (asArray) {
|
720
|
+
if (args->asArray) {
|
400
721
|
rb_ary_push(rowVal, val);
|
401
722
|
} else {
|
402
723
|
rb_hash_aset(rowVal, field, val);
|
403
724
|
}
|
404
725
|
} else {
|
405
|
-
if (asArray) {
|
726
|
+
if (args->asArray) {
|
406
727
|
rb_ary_push(rowVal, Qnil);
|
407
728
|
} else {
|
408
729
|
rb_hash_aset(rowVal, field, Qnil);
|
@@ -413,12 +734,11 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
413
734
|
}
|
414
735
|
|
415
736
|
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
416
|
-
mysql2_result_wrapper * wrapper;
|
417
737
|
unsigned int i = 0;
|
418
738
|
short int symbolizeKeys = 0;
|
419
739
|
VALUE defaults;
|
420
740
|
|
421
|
-
|
741
|
+
GET_RESULT(self);
|
422
742
|
|
423
743
|
defaults = rb_iv_get(self, "@query_options");
|
424
744
|
Check_Type(defaults, T_HASH);
|
@@ -431,7 +751,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
431
751
|
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
432
752
|
}
|
433
753
|
|
434
|
-
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
754
|
+
if ((my_ulonglong)RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
435
755
|
for (i=0; i<wrapper->numberOfFields; i++) {
|
436
756
|
rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
437
757
|
}
|
@@ -440,108 +760,38 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
440
760
|
return wrapper->fields;
|
441
761
|
}
|
442
762
|
|
443
|
-
static VALUE
|
444
|
-
|
445
|
-
|
446
|
-
|
763
|
+
static VALUE rb_mysql_result_each_(VALUE self,
|
764
|
+
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
765
|
+
const result_each_args *args)
|
766
|
+
{
|
447
767
|
unsigned long i;
|
448
|
-
const char *
|
449
|
-
|
450
|
-
MYSQL_FIELD * fields = NULL;
|
768
|
+
const char *errstr;
|
769
|
+
MYSQL_FIELD *fields = NULL;
|
451
770
|
|
452
|
-
|
453
|
-
|
454
|
-
defaults = rb_iv_get(self, "@query_options");
|
455
|
-
Check_Type(defaults, T_HASH);
|
456
|
-
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
457
|
-
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
458
|
-
} else {
|
459
|
-
opts = defaults;
|
460
|
-
}
|
461
|
-
|
462
|
-
if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
|
463
|
-
symbolizeKeys = 1;
|
464
|
-
}
|
465
|
-
|
466
|
-
if (rb_hash_aref(opts, sym_as) == sym_array) {
|
467
|
-
asArray = 1;
|
468
|
-
}
|
771
|
+
GET_RESULT(self);
|
469
772
|
|
470
|
-
if (
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
|
475
|
-
cacheRows = 0;
|
476
|
-
}
|
477
|
-
|
478
|
-
if (rb_hash_aref(opts, sym_cast) == Qfalse) {
|
479
|
-
cast = 0;
|
480
|
-
}
|
481
|
-
|
482
|
-
if (rb_hash_aref(opts, sym_stream) == Qtrue) {
|
483
|
-
streaming = 1;
|
484
|
-
}
|
485
|
-
|
486
|
-
if (streaming && cacheRows) {
|
487
|
-
rb_warn("cacheRows is ignored if streaming is true");
|
488
|
-
}
|
489
|
-
|
490
|
-
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
491
|
-
if (dbTz == sym_local) {
|
492
|
-
db_timezone = intern_local;
|
493
|
-
} else if (dbTz == sym_utc) {
|
494
|
-
db_timezone = intern_utc;
|
495
|
-
} else {
|
496
|
-
if (!NIL_P(dbTz)) {
|
497
|
-
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
498
|
-
}
|
499
|
-
db_timezone = intern_local;
|
500
|
-
}
|
501
|
-
|
502
|
-
appTz = rb_hash_aref(opts, sym_application_timezone);
|
503
|
-
if (appTz == sym_local) {
|
504
|
-
app_timezone = intern_local;
|
505
|
-
} else if (appTz == sym_utc) {
|
506
|
-
app_timezone = intern_utc;
|
507
|
-
} else {
|
508
|
-
app_timezone = Qnil;
|
509
|
-
}
|
510
|
-
|
511
|
-
if (wrapper->lastRowProcessed == 0) {
|
512
|
-
if (streaming) {
|
513
|
-
/* We can't get number of rows if we're streaming, */
|
514
|
-
/* until we've finished fetching all rows */
|
515
|
-
wrapper->numberOfRows = 0;
|
773
|
+
if (wrapper->is_streaming) {
|
774
|
+
/* When streaming, we will only yield rows, not return them. */
|
775
|
+
if (wrapper->rows == Qnil) {
|
516
776
|
wrapper->rows = rb_ary_new();
|
517
|
-
} else {
|
518
|
-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
519
|
-
if (wrapper->numberOfRows == 0) {
|
520
|
-
wrapper->rows = rb_ary_new();
|
521
|
-
return wrapper->rows;
|
522
|
-
}
|
523
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
524
777
|
}
|
525
|
-
}
|
526
778
|
|
527
|
-
if (streaming) {
|
528
779
|
if (!wrapper->streamingComplete) {
|
529
780
|
VALUE row;
|
530
781
|
|
531
782
|
fields = mysql_fetch_fields(wrapper->result);
|
532
783
|
|
533
784
|
do {
|
534
|
-
row =
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
785
|
+
row = fetch_row_func(self, fields, args);
|
786
|
+
if (row != Qnil) {
|
787
|
+
wrapper->numberOfRows++;
|
788
|
+
if (args->block_given != Qnil) {
|
789
|
+
rb_yield(row);
|
790
|
+
}
|
539
791
|
}
|
540
792
|
} while(row != Qnil);
|
541
793
|
|
542
794
|
rb_mysql_result_free_result(wrapper);
|
543
|
-
|
544
|
-
wrapper->numberOfRows = wrapper->lastRowProcessed;
|
545
795
|
wrapper->streamingComplete = 1;
|
546
796
|
|
547
797
|
// Check for errors, the connection might have gone out from under us
|
@@ -554,7 +804,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
554
804
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
555
805
|
}
|
556
806
|
} else {
|
557
|
-
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
807
|
+
if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
558
808
|
/* we've already read the entire dataset from the C result into our */
|
559
809
|
/* internal array. Lets hand that over to the user since it's ready to go */
|
560
810
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
@@ -567,11 +817,11 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
567
817
|
|
568
818
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
569
819
|
VALUE row;
|
570
|
-
if (cacheRows && i < rowsProcessed) {
|
820
|
+
if (args->cacheRows && i < rowsProcessed) {
|
571
821
|
row = rb_ary_entry(wrapper->rows, i);
|
572
822
|
} else {
|
573
|
-
row =
|
574
|
-
if (cacheRows) {
|
823
|
+
row = fetch_row_func(self, fields, args);
|
824
|
+
if (args->cacheRows) {
|
575
825
|
rb_ary_store(wrapper->rows, i, row);
|
576
826
|
}
|
577
827
|
wrapper->lastRowProcessed++;
|
@@ -583,7 +833,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
583
833
|
return Qnil;
|
584
834
|
}
|
585
835
|
|
586
|
-
if (
|
836
|
+
if (args->block_given != Qnil) {
|
587
837
|
rb_yield(row);
|
588
838
|
}
|
589
839
|
}
|
@@ -594,28 +844,124 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
594
844
|
}
|
595
845
|
}
|
596
846
|
|
847
|
+
// FIXME return Enumerator instead?
|
848
|
+
// return rb_ary_each(wrapper->rows);
|
597
849
|
return wrapper->rows;
|
598
850
|
}
|
599
851
|
|
852
|
+
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
853
|
+
result_each_args args;
|
854
|
+
VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
855
|
+
ID db_timezone, app_timezone, dbTz, appTz;
|
856
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
857
|
+
|
858
|
+
GET_RESULT(self);
|
859
|
+
|
860
|
+
if (wrapper->stmt_wrapper && wrapper->stmt_wrapper->closed) {
|
861
|
+
rb_raise(cMysql2Error, "Statement handle already closed");
|
862
|
+
}
|
863
|
+
|
864
|
+
defaults = rb_iv_get(self, "@query_options");
|
865
|
+
Check_Type(defaults, T_HASH);
|
866
|
+
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
867
|
+
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
868
|
+
} else {
|
869
|
+
opts = defaults;
|
870
|
+
}
|
871
|
+
|
872
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
873
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
874
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
875
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
876
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
877
|
+
|
878
|
+
if (wrapper->is_streaming && cacheRows) {
|
879
|
+
rb_warn(":cache_rows is ignored if :stream is true");
|
880
|
+
}
|
881
|
+
|
882
|
+
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
883
|
+
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
884
|
+
}
|
885
|
+
|
886
|
+
if (wrapper->stmt_wrapper && !cast) {
|
887
|
+
rb_warn(":cast is forced for prepared statements");
|
888
|
+
}
|
889
|
+
|
890
|
+
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
891
|
+
if (dbTz == sym_local) {
|
892
|
+
db_timezone = intern_local;
|
893
|
+
} else if (dbTz == sym_utc) {
|
894
|
+
db_timezone = intern_utc;
|
895
|
+
} else {
|
896
|
+
if (!NIL_P(dbTz)) {
|
897
|
+
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
898
|
+
}
|
899
|
+
db_timezone = intern_local;
|
900
|
+
}
|
901
|
+
|
902
|
+
appTz = rb_hash_aref(opts, sym_application_timezone);
|
903
|
+
if (appTz == sym_local) {
|
904
|
+
app_timezone = intern_local;
|
905
|
+
} else if (appTz == sym_utc) {
|
906
|
+
app_timezone = intern_utc;
|
907
|
+
} else {
|
908
|
+
app_timezone = Qnil;
|
909
|
+
}
|
910
|
+
|
911
|
+
if (wrapper->lastRowProcessed == 0 && !wrapper->is_streaming) {
|
912
|
+
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;
|
916
|
+
}
|
917
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
918
|
+
}
|
919
|
+
|
920
|
+
// Backward compat
|
921
|
+
args.symbolizeKeys = symbolizeKeys;
|
922
|
+
args.asArray = asArray;
|
923
|
+
args.castBool = castBool;
|
924
|
+
args.cacheRows = cacheRows;
|
925
|
+
args.cast = cast;
|
926
|
+
args.db_timezone = db_timezone;
|
927
|
+
args.app_timezone = app_timezone;
|
928
|
+
args.block_given = block;
|
929
|
+
|
930
|
+
if (wrapper->stmt_wrapper) {
|
931
|
+
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
932
|
+
} else {
|
933
|
+
fetch_row_func = rb_mysql_result_fetch_row;
|
934
|
+
}
|
935
|
+
|
936
|
+
return rb_mysql_result_each_(self, fetch_row_func, &args);
|
937
|
+
}
|
938
|
+
|
600
939
|
static VALUE rb_mysql_result_count(VALUE self) {
|
601
|
-
|
940
|
+
GET_RESULT(self);
|
941
|
+
|
942
|
+
if (wrapper->is_streaming) {
|
943
|
+
/* This is an unsigned long per result.h */
|
944
|
+
return ULONG2NUM(wrapper->numberOfRows);
|
945
|
+
}
|
602
946
|
|
603
|
-
GetMysql2Result(self, wrapper);
|
604
947
|
if (wrapper->resultFreed) {
|
605
|
-
|
606
|
-
|
948
|
+
/* Ruby arrays have platform signed long length */
|
949
|
+
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
950
|
+
} else {
|
951
|
+
/* MySQL returns an unsigned 64-bit long here */
|
952
|
+
if (wrapper->stmt_wrapper) {
|
953
|
+
return ULL2NUM(mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt));
|
607
954
|
} else {
|
608
|
-
return
|
955
|
+
return ULL2NUM(mysql_num_rows(wrapper->result));
|
609
956
|
}
|
610
|
-
} else {
|
611
|
-
return INT2FIX(mysql_num_rows(wrapper->result));
|
612
957
|
}
|
613
958
|
}
|
614
959
|
|
615
960
|
/* Mysql2::Result */
|
616
|
-
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
961
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement) {
|
617
962
|
VALUE obj;
|
618
963
|
mysql2_result_wrapper * wrapper;
|
964
|
+
|
619
965
|
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
620
966
|
wrapper->numberOfFields = 0;
|
621
967
|
wrapper->numberOfRows = 0;
|
@@ -629,11 +975,27 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
629
975
|
wrapper->client = client;
|
630
976
|
wrapper->client_wrapper = DATA_PTR(client);
|
631
977
|
wrapper->client_wrapper->refcount++;
|
978
|
+
wrapper->result_buffers = NULL;
|
979
|
+
wrapper->is_null = NULL;
|
980
|
+
wrapper->error = NULL;
|
981
|
+
wrapper->length = NULL;
|
982
|
+
|
983
|
+
/* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
|
984
|
+
wrapper->statement = statement;
|
985
|
+
if (statement != Qnil) {
|
986
|
+
wrapper->stmt_wrapper = DATA_PTR(statement);
|
987
|
+
wrapper->stmt_wrapper->refcount++;
|
988
|
+
} else {
|
989
|
+
wrapper->stmt_wrapper = NULL;
|
990
|
+
}
|
632
991
|
|
633
992
|
rb_obj_call_init(obj, 0, NULL);
|
634
|
-
|
635
993
|
rb_iv_set(obj, "@query_options", options);
|
636
994
|
|
995
|
+
/* Options that cannot be changed in results.each(...) { |row| }
|
996
|
+
* should be processed here. */
|
997
|
+
wrapper->is_streaming = (rb_hash_aref(options, sym_stream) == Qtrue ? 1 : 0);
|
998
|
+
|
637
999
|
return obj;
|
638
1000
|
}
|
639
1001
|
|