mysql2 0.3.18 → 0.4.7
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 +135 -55
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +368 -197
- data/ext/mysql2/client.h +13 -3
- data/ext/mysql2/extconf.rb +118 -35
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +7 -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 +512 -138
- data/ext/mysql2/result.h +13 -6
- data/ext/mysql2/statement.c +585 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +85 -26
- 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/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +22 -21
- data/spec/mysql2/client_spec.rb +525 -388
- data/spec/mysql2/error_spec.rb +38 -39
- data/spec/mysql2/result_spec.rb +223 -214
- data/spec/mysql2/statement_spec.rb +757 -0
- data/spec/spec_helper.rb +80 -59
- 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 +42 -47
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,50 @@ 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->statement != Qnil) {
|
108
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
109
|
+
}
|
110
|
+
|
111
|
+
if (wrapper->result_buffers) {
|
112
|
+
unsigned int i;
|
113
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
114
|
+
if (wrapper->result_buffers[i].buffer) {
|
115
|
+
xfree(wrapper->result_buffers[i].buffer);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
xfree(wrapper->result_buffers);
|
119
|
+
xfree(wrapper->is_null);
|
120
|
+
xfree(wrapper->error);
|
121
|
+
xfree(wrapper->length);
|
122
|
+
}
|
123
|
+
/* Clue that the next statement execute will need to allocate a new result buffer. */
|
124
|
+
wrapper->result_buffers = NULL;
|
125
|
+
}
|
75
126
|
/* FIXME: this may call flush_use_result, which can hit the socket */
|
127
|
+
/* For prepared statements, wrapper->result is the result metadata */
|
76
128
|
mysql_free_result(wrapper->result);
|
77
129
|
wrapper->resultFreed = 1;
|
78
130
|
}
|
@@ -80,7 +132,7 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
80
132
|
|
81
133
|
/* this is called during GC */
|
82
134
|
static void rb_mysql_result_free(void *ptr) {
|
83
|
-
mysql2_result_wrapper *
|
135
|
+
mysql2_result_wrapper *wrapper = ptr;
|
84
136
|
rb_mysql_result_free_result(wrapper);
|
85
137
|
|
86
138
|
// If the GC gets to client first it will be nil
|
@@ -91,6 +143,12 @@ static void rb_mysql_result_free(void *ptr) {
|
|
91
143
|
xfree(wrapper);
|
92
144
|
}
|
93
145
|
|
146
|
+
static VALUE rb_mysql_result_free_(VALUE self) {
|
147
|
+
GET_RESULT(self);
|
148
|
+
rb_mysql_result_free_result(wrapper);
|
149
|
+
return Qnil;
|
150
|
+
}
|
151
|
+
|
94
152
|
/*
|
95
153
|
* for small results, this won't hit the network, but there's no
|
96
154
|
* reliable way for us to tell this so we'll always release the GVL
|
@@ -102,10 +160,16 @@ static void *nogvl_fetch_row(void *ptr) {
|
|
102
160
|
return mysql_fetch_row(result);
|
103
161
|
}
|
104
162
|
|
105
|
-
static
|
106
|
-
|
163
|
+
static void *nogvl_stmt_fetch(void *ptr) {
|
164
|
+
MYSQL_STMT *stmt = ptr;
|
165
|
+
uintptr_t r = mysql_stmt_fetch(stmt);
|
166
|
+
|
167
|
+
return (void *)r;
|
168
|
+
}
|
169
|
+
|
170
|
+
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
|
107
171
|
VALUE rb_field;
|
108
|
-
|
172
|
+
GET_RESULT(self);
|
109
173
|
|
110
174
|
if (wrapper->fields == Qnil) {
|
111
175
|
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
@@ -147,7 +211,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
147
211
|
|
148
212
|
#ifdef HAVE_RUBY_ENCODING_H
|
149
213
|
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
|
214
|
+
/* if binary flag is set, respect its wishes */
|
151
215
|
if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
|
152
216
|
rb_enc_associate(val, binaryEncoding);
|
153
217
|
} else if (!field.charsetnr) {
|
@@ -182,7 +246,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
182
246
|
*/
|
183
247
|
static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
184
248
|
{
|
185
|
-
|
249
|
+
size_t i;
|
186
250
|
for (i = 0; i < (len - 1); i++) {
|
187
251
|
if (msec_char[i] == '\0') {
|
188
252
|
msec_char[i] = '0';
|
@@ -191,9 +255,271 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
191
255
|
return (unsigned int)strtoul(msec_char, NULL, 10);
|
192
256
|
}
|
193
257
|
|
194
|
-
static
|
258
|
+
static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) {
|
259
|
+
unsigned int i;
|
260
|
+
GET_RESULT(self);
|
261
|
+
|
262
|
+
if (wrapper->result_buffers != NULL) return;
|
263
|
+
|
264
|
+
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
265
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
266
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(bool));
|
267
|
+
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
268
|
+
|
269
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
270
|
+
wrapper->result_buffers[i].buffer_type = fields[i].type;
|
271
|
+
|
272
|
+
// mysql type | C type
|
273
|
+
switch(fields[i].type) {
|
274
|
+
case MYSQL_TYPE_NULL: // NULL
|
275
|
+
break;
|
276
|
+
case MYSQL_TYPE_TINY: // signed char
|
277
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(signed char));
|
278
|
+
wrapper->result_buffers[i].buffer_length = sizeof(signed char);
|
279
|
+
break;
|
280
|
+
case MYSQL_TYPE_SHORT: // short int
|
281
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
|
282
|
+
wrapper->result_buffers[i].buffer_length = sizeof(short int);
|
283
|
+
break;
|
284
|
+
case MYSQL_TYPE_INT24: // int
|
285
|
+
case MYSQL_TYPE_LONG: // int
|
286
|
+
case MYSQL_TYPE_YEAR: // int
|
287
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
|
288
|
+
wrapper->result_buffers[i].buffer_length = sizeof(int);
|
289
|
+
break;
|
290
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
291
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(long long int));
|
292
|
+
wrapper->result_buffers[i].buffer_length = sizeof(long long int);
|
293
|
+
break;
|
294
|
+
case MYSQL_TYPE_FLOAT: // float
|
295
|
+
case MYSQL_TYPE_DOUBLE: // double
|
296
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(double));
|
297
|
+
wrapper->result_buffers[i].buffer_length = sizeof(double);
|
298
|
+
break;
|
299
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
300
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
301
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
302
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
303
|
+
case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
|
304
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(MYSQL_TIME));
|
305
|
+
wrapper->result_buffers[i].buffer_length = sizeof(MYSQL_TIME);
|
306
|
+
break;
|
307
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
308
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
309
|
+
case MYSQL_TYPE_STRING: // char[]
|
310
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
311
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
312
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
313
|
+
case MYSQL_TYPE_BLOB: // char[]
|
314
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
315
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
316
|
+
case MYSQL_TYPE_BIT: // char[]
|
317
|
+
case MYSQL_TYPE_SET: // char[]
|
318
|
+
case MYSQL_TYPE_ENUM: // char[]
|
319
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
320
|
+
default:
|
321
|
+
wrapper->result_buffers[i].buffer = xmalloc(fields[i].max_length);
|
322
|
+
wrapper->result_buffers[i].buffer_length = fields[i].max_length;
|
323
|
+
break;
|
324
|
+
}
|
325
|
+
|
326
|
+
wrapper->result_buffers[i].is_null = &wrapper->is_null[i];
|
327
|
+
wrapper->result_buffers[i].length = &wrapper->length[i];
|
328
|
+
wrapper->result_buffers[i].error = &wrapper->error[i];
|
329
|
+
wrapper->result_buffers[i].is_unsigned = ((fields[i].flags & UNSIGNED_FLAG) != 0);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
334
|
+
{
|
335
|
+
VALUE rowVal;
|
336
|
+
unsigned int i = 0;
|
337
|
+
|
338
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
339
|
+
rb_encoding *default_internal_enc;
|
340
|
+
rb_encoding *conn_enc;
|
341
|
+
#endif
|
342
|
+
GET_RESULT(self);
|
343
|
+
|
344
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
345
|
+
default_internal_enc = rb_default_internal_encoding();
|
346
|
+
conn_enc = rb_to_encoding(wrapper->encoding);
|
347
|
+
#endif
|
348
|
+
|
349
|
+
if (wrapper->fields == Qnil) {
|
350
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
351
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
352
|
+
}
|
353
|
+
if (args->asArray) {
|
354
|
+
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
355
|
+
} else {
|
356
|
+
rowVal = rb_hash_new();
|
357
|
+
}
|
358
|
+
|
359
|
+
if (wrapper->result_buffers == NULL) {
|
360
|
+
rb_mysql_result_alloc_result_buffers(self, fields);
|
361
|
+
}
|
362
|
+
|
363
|
+
if (mysql_stmt_bind_result(wrapper->stmt_wrapper->stmt, wrapper->result_buffers)) {
|
364
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
365
|
+
}
|
366
|
+
|
367
|
+
{
|
368
|
+
switch((uintptr_t)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper->stmt_wrapper->stmt, RUBY_UBF_IO, 0)) {
|
369
|
+
case 0:
|
370
|
+
/* success */
|
371
|
+
break;
|
372
|
+
|
373
|
+
case 1:
|
374
|
+
/* error */
|
375
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
376
|
+
|
377
|
+
case MYSQL_NO_DATA:
|
378
|
+
/* no more row */
|
379
|
+
return Qnil;
|
380
|
+
|
381
|
+
case MYSQL_DATA_TRUNCATED:
|
382
|
+
rb_raise(cMysql2Error, "IMPLBUG: caught MYSQL_DATA_TRUNCATED. should not come here as buffer_length is set to fields[i].max_length.");
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
387
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
388
|
+
VALUE val = Qnil;
|
389
|
+
MYSQL_TIME *ts;
|
390
|
+
|
391
|
+
if (wrapper->is_null[i]) {
|
392
|
+
val = Qnil;
|
393
|
+
} else {
|
394
|
+
const MYSQL_BIND* const result_buffer = &wrapper->result_buffers[i];
|
395
|
+
|
396
|
+
switch(result_buffer->buffer_type) {
|
397
|
+
case MYSQL_TYPE_TINY: // signed char
|
398
|
+
if (args->castBool && fields[i].length == 1) {
|
399
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
400
|
+
break;
|
401
|
+
}
|
402
|
+
if (result_buffer->is_unsigned) {
|
403
|
+
val = UINT2NUM(*((unsigned char*)result_buffer->buffer));
|
404
|
+
} else {
|
405
|
+
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
406
|
+
}
|
407
|
+
break;
|
408
|
+
case MYSQL_TYPE_SHORT: // short int
|
409
|
+
if (result_buffer->is_unsigned) {
|
410
|
+
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
411
|
+
} else {
|
412
|
+
val = INT2NUM(*((short int*)result_buffer->buffer));
|
413
|
+
}
|
414
|
+
break;
|
415
|
+
case MYSQL_TYPE_INT24: // int
|
416
|
+
case MYSQL_TYPE_LONG: // int
|
417
|
+
case MYSQL_TYPE_YEAR: // int
|
418
|
+
if (result_buffer->is_unsigned) {
|
419
|
+
val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
|
420
|
+
} else {
|
421
|
+
val = INT2NUM(*((int*)result_buffer->buffer));
|
422
|
+
}
|
423
|
+
break;
|
424
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
425
|
+
if (result_buffer->is_unsigned) {
|
426
|
+
val = ULL2NUM(*((unsigned long long int*)result_buffer->buffer));
|
427
|
+
} else {
|
428
|
+
val = LL2NUM(*((long long int*)result_buffer->buffer));
|
429
|
+
}
|
430
|
+
break;
|
431
|
+
case MYSQL_TYPE_FLOAT: // float
|
432
|
+
val = rb_float_new((double)(*((float*)result_buffer->buffer)));
|
433
|
+
break;
|
434
|
+
case MYSQL_TYPE_DOUBLE: // double
|
435
|
+
val = rb_float_new((double)(*((double*)result_buffer->buffer)));
|
436
|
+
break;
|
437
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
438
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
439
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
440
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day));
|
441
|
+
break;
|
442
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
443
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
444
|
+
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));
|
445
|
+
if (!NIL_P(args->app_timezone)) {
|
446
|
+
if (args->app_timezone == intern_local) {
|
447
|
+
val = rb_funcall(val, intern_localtime, 0);
|
448
|
+
} else { // utc
|
449
|
+
val = rb_funcall(val, intern_utc, 0);
|
450
|
+
}
|
451
|
+
}
|
452
|
+
break;
|
453
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
454
|
+
case MYSQL_TYPE_TIMESTAMP: { // MYSQL_TIME
|
455
|
+
uint64_t seconds;
|
456
|
+
|
457
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
458
|
+
seconds = (ts->year*31557600ULL) + (ts->month*2592000ULL) + (ts->day*86400ULL) + (ts->hour*3600ULL) + (ts->minute*60ULL) + ts->second;
|
459
|
+
|
460
|
+
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
|
461
|
+
VALUE offset = INT2NUM(0);
|
462
|
+
if (args->db_timezone == intern_local) {
|
463
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
464
|
+
}
|
465
|
+
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);
|
466
|
+
if (!NIL_P(args->app_timezone)) {
|
467
|
+
if (args->app_timezone == intern_local) {
|
468
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
469
|
+
val = rb_funcall(val, intern_new_offset, 1, offset);
|
470
|
+
} else { // utc
|
471
|
+
val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
|
472
|
+
}
|
473
|
+
}
|
474
|
+
} else {
|
475
|
+
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));
|
476
|
+
if (!NIL_P(args->app_timezone)) {
|
477
|
+
if (args->app_timezone == intern_local) {
|
478
|
+
val = rb_funcall(val, intern_localtime, 0);
|
479
|
+
} else { // utc
|
480
|
+
val = rb_funcall(val, intern_utc, 0);
|
481
|
+
}
|
482
|
+
}
|
483
|
+
}
|
484
|
+
break;
|
485
|
+
}
|
486
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
487
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
488
|
+
val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
|
489
|
+
break;
|
490
|
+
case MYSQL_TYPE_STRING: // char[]
|
491
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
492
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
493
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
494
|
+
case MYSQL_TYPE_BLOB: // char[]
|
495
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
496
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
497
|
+
case MYSQL_TYPE_BIT: // char[]
|
498
|
+
case MYSQL_TYPE_SET: // char[]
|
499
|
+
case MYSQL_TYPE_ENUM: // char[]
|
500
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
501
|
+
default:
|
502
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
503
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
504
|
+
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
505
|
+
#endif
|
506
|
+
break;
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
if (args->asArray) {
|
511
|
+
rb_ary_push(rowVal, val);
|
512
|
+
} else {
|
513
|
+
rb_hash_aset(rowVal, field, val);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
|
517
|
+
return rowVal;
|
518
|
+
}
|
519
|
+
|
520
|
+
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
521
|
+
{
|
195
522
|
VALUE rowVal;
|
196
|
-
mysql2_result_wrapper * wrapper;
|
197
523
|
MYSQL_ROW row;
|
198
524
|
unsigned int i = 0;
|
199
525
|
unsigned long * fieldLengths;
|
@@ -202,7 +528,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
202
528
|
rb_encoding *default_internal_enc;
|
203
529
|
rb_encoding *conn_enc;
|
204
530
|
#endif
|
205
|
-
|
531
|
+
GET_RESULT(self);
|
206
532
|
|
207
533
|
#ifdef HAVE_RUBY_ENCODING_H
|
208
534
|
default_internal_enc = rb_default_internal_encoding();
|
@@ -215,24 +541,24 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
215
541
|
return Qnil;
|
216
542
|
}
|
217
543
|
|
218
|
-
if (
|
544
|
+
if (wrapper->fields == Qnil) {
|
545
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
546
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
547
|
+
}
|
548
|
+
if (args->asArray) {
|
219
549
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
220
550
|
} else {
|
221
551
|
rowVal = rb_hash_new();
|
222
552
|
}
|
223
553
|
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
224
|
-
if (wrapper->fields == Qnil) {
|
225
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
226
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
227
|
-
}
|
228
554
|
|
229
555
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
230
|
-
VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
556
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
231
557
|
if (row[i]) {
|
232
558
|
VALUE val = Qnil;
|
233
559
|
enum enum_field_types type = fields[i].type;
|
234
560
|
|
235
|
-
if (!cast) {
|
561
|
+
if (!args->cast) {
|
236
562
|
if (type == MYSQL_TYPE_NULL) {
|
237
563
|
val = Qnil;
|
238
564
|
} else {
|
@@ -247,14 +573,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
247
573
|
val = Qnil;
|
248
574
|
break;
|
249
575
|
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
250
|
-
if (castBool && fields[i].length == 1) {
|
576
|
+
if (args->castBool && fields[i].length == 1) {
|
251
577
|
val = *row[i] == 1 ? Qtrue : Qfalse;
|
252
578
|
}else{
|
253
579
|
val = rb_str_new(row[i], fieldLengths[i]);
|
254
580
|
}
|
255
581
|
break;
|
256
582
|
case MYSQL_TYPE_TINY: /* TINYINT field */
|
257
|
-
if (castBool && fields[i].length == 1) {
|
583
|
+
if (args->castBool && fields[i].length == 1) {
|
258
584
|
val = *row[i] != '0' ? Qtrue : Qfalse;
|
259
585
|
break;
|
260
586
|
}
|
@@ -297,9 +623,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
297
623
|
break;
|
298
624
|
}
|
299
625
|
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) {
|
626
|
+
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));
|
627
|
+
if (!NIL_P(args->app_timezone)) {
|
628
|
+
if (args->app_timezone == intern_local) {
|
303
629
|
val = rb_funcall(val, intern_localtime, 0);
|
304
630
|
} else { /* utc */
|
305
631
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -330,12 +656,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
330
656
|
} else {
|
331
657
|
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
|
332
658
|
VALUE offset = INT2NUM(0);
|
333
|
-
if (db_timezone == intern_local) {
|
659
|
+
if (args->db_timezone == intern_local) {
|
334
660
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
335
661
|
}
|
336
662
|
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) {
|
663
|
+
if (!NIL_P(args->app_timezone)) {
|
664
|
+
if (args->app_timezone == intern_local) {
|
339
665
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
340
666
|
val = rb_funcall(val, intern_new_offset, 1, offset);
|
341
667
|
} else { /* utc */
|
@@ -344,9 +670,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
344
670
|
}
|
345
671
|
} else {
|
346
672
|
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) {
|
673
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
674
|
+
if (!NIL_P(args->app_timezone)) {
|
675
|
+
if (args->app_timezone == intern_local) {
|
350
676
|
val = rb_funcall(val, intern_localtime, 0);
|
351
677
|
} else { /* utc */
|
352
678
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -396,13 +722,13 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
396
722
|
break;
|
397
723
|
}
|
398
724
|
}
|
399
|
-
if (asArray) {
|
725
|
+
if (args->asArray) {
|
400
726
|
rb_ary_push(rowVal, val);
|
401
727
|
} else {
|
402
728
|
rb_hash_aset(rowVal, field, val);
|
403
729
|
}
|
404
730
|
} else {
|
405
|
-
if (asArray) {
|
731
|
+
if (args->asArray) {
|
406
732
|
rb_ary_push(rowVal, Qnil);
|
407
733
|
} else {
|
408
734
|
rb_hash_aset(rowVal, field, Qnil);
|
@@ -413,12 +739,11 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
413
739
|
}
|
414
740
|
|
415
741
|
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
416
|
-
mysql2_result_wrapper * wrapper;
|
417
742
|
unsigned int i = 0;
|
418
743
|
short int symbolizeKeys = 0;
|
419
744
|
VALUE defaults;
|
420
745
|
|
421
|
-
|
746
|
+
GET_RESULT(self);
|
422
747
|
|
423
748
|
defaults = rb_iv_get(self, "@query_options");
|
424
749
|
Check_Type(defaults, T_HASH);
|
@@ -431,7 +756,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
431
756
|
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
432
757
|
}
|
433
758
|
|
434
|
-
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
759
|
+
if ((my_ulonglong)RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
435
760
|
for (i=0; i<wrapper->numberOfFields; i++) {
|
436
761
|
rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
437
762
|
}
|
@@ -440,108 +765,38 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
440
765
|
return wrapper->fields;
|
441
766
|
}
|
442
767
|
|
443
|
-
static VALUE
|
444
|
-
|
445
|
-
|
446
|
-
|
768
|
+
static VALUE rb_mysql_result_each_(VALUE self,
|
769
|
+
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
770
|
+
const result_each_args *args)
|
771
|
+
{
|
447
772
|
unsigned long i;
|
448
|
-
const char *
|
449
|
-
|
450
|
-
MYSQL_FIELD * fields = NULL;
|
451
|
-
|
452
|
-
GetMysql2Result(self, wrapper);
|
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
|
-
}
|
469
|
-
|
470
|
-
if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
|
471
|
-
castBool = 1;
|
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
|
-
}
|
773
|
+
const char *errstr;
|
774
|
+
MYSQL_FIELD *fields = NULL;
|
489
775
|
|
490
|
-
|
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
|
-
}
|
776
|
+
GET_RESULT(self);
|
510
777
|
|
511
|
-
if (wrapper->
|
512
|
-
|
513
|
-
|
514
|
-
/* until we've finished fetching all rows */
|
515
|
-
wrapper->numberOfRows = 0;
|
778
|
+
if (wrapper->is_streaming) {
|
779
|
+
/* When streaming, we will only yield rows, not return them. */
|
780
|
+
if (wrapper->rows == Qnil) {
|
516
781
|
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
782
|
}
|
525
|
-
}
|
526
783
|
|
527
|
-
if (streaming) {
|
528
784
|
if (!wrapper->streamingComplete) {
|
529
785
|
VALUE row;
|
530
786
|
|
531
787
|
fields = mysql_fetch_fields(wrapper->result);
|
532
788
|
|
533
789
|
do {
|
534
|
-
row =
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
790
|
+
row = fetch_row_func(self, fields, args);
|
791
|
+
if (row != Qnil) {
|
792
|
+
wrapper->numberOfRows++;
|
793
|
+
if (args->block_given != Qnil) {
|
794
|
+
rb_yield(row);
|
795
|
+
}
|
539
796
|
}
|
540
797
|
} while(row != Qnil);
|
541
798
|
|
542
799
|
rb_mysql_result_free_result(wrapper);
|
543
|
-
|
544
|
-
wrapper->numberOfRows = wrapper->lastRowProcessed;
|
545
800
|
wrapper->streamingComplete = 1;
|
546
801
|
|
547
802
|
// Check for errors, the connection might have gone out from under us
|
@@ -554,7 +809,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
554
809
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
555
810
|
}
|
556
811
|
} else {
|
557
|
-
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
812
|
+
if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
558
813
|
/* we've already read the entire dataset from the C result into our */
|
559
814
|
/* internal array. Lets hand that over to the user since it's ready to go */
|
560
815
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
@@ -567,11 +822,11 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
567
822
|
|
568
823
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
569
824
|
VALUE row;
|
570
|
-
if (cacheRows && i < rowsProcessed) {
|
825
|
+
if (args->cacheRows && i < rowsProcessed) {
|
571
826
|
row = rb_ary_entry(wrapper->rows, i);
|
572
827
|
} else {
|
573
|
-
row =
|
574
|
-
if (cacheRows) {
|
828
|
+
row = fetch_row_func(self, fields, args);
|
829
|
+
if (args->cacheRows) {
|
575
830
|
rb_ary_store(wrapper->rows, i, row);
|
576
831
|
}
|
577
832
|
wrapper->lastRowProcessed++;
|
@@ -579,43 +834,145 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
579
834
|
|
580
835
|
if (row == Qnil) {
|
581
836
|
/* we don't need the mysql C dataset around anymore, peace it */
|
582
|
-
|
837
|
+
if (args->cacheRows) {
|
838
|
+
rb_mysql_result_free_result(wrapper);
|
839
|
+
}
|
583
840
|
return Qnil;
|
584
841
|
}
|
585
842
|
|
586
|
-
if (
|
843
|
+
if (args->block_given != Qnil) {
|
587
844
|
rb_yield(row);
|
588
845
|
}
|
589
846
|
}
|
590
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
847
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
|
591
848
|
/* we don't need the mysql C dataset around anymore, peace it */
|
592
849
|
rb_mysql_result_free_result(wrapper);
|
593
850
|
}
|
594
851
|
}
|
595
852
|
}
|
596
853
|
|
854
|
+
// FIXME return Enumerator instead?
|
855
|
+
// return rb_ary_each(wrapper->rows);
|
597
856
|
return wrapper->rows;
|
598
857
|
}
|
599
858
|
|
859
|
+
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
860
|
+
result_each_args args;
|
861
|
+
VALUE defaults, opts, block, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
862
|
+
ID db_timezone, app_timezone, dbTz, appTz;
|
863
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
864
|
+
|
865
|
+
GET_RESULT(self);
|
866
|
+
|
867
|
+
if (wrapper->stmt_wrapper && wrapper->stmt_wrapper->closed) {
|
868
|
+
rb_raise(cMysql2Error, "Statement handle already closed");
|
869
|
+
}
|
870
|
+
|
871
|
+
defaults = rb_iv_get(self, "@query_options");
|
872
|
+
Check_Type(defaults, T_HASH);
|
873
|
+
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
874
|
+
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
875
|
+
} else {
|
876
|
+
opts = defaults;
|
877
|
+
}
|
878
|
+
|
879
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
880
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
881
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
882
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
883
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
884
|
+
|
885
|
+
if (wrapper->is_streaming && cacheRows) {
|
886
|
+
rb_warn(":cache_rows is ignored if :stream is true");
|
887
|
+
}
|
888
|
+
|
889
|
+
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
890
|
+
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
891
|
+
cacheRows = 1;
|
892
|
+
}
|
893
|
+
|
894
|
+
if (wrapper->stmt_wrapper && !cast) {
|
895
|
+
rb_warn(":cast is forced for prepared statements");
|
896
|
+
}
|
897
|
+
|
898
|
+
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
899
|
+
if (dbTz == sym_local) {
|
900
|
+
db_timezone = intern_local;
|
901
|
+
} else if (dbTz == sym_utc) {
|
902
|
+
db_timezone = intern_utc;
|
903
|
+
} else {
|
904
|
+
if (!NIL_P(dbTz)) {
|
905
|
+
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
906
|
+
}
|
907
|
+
db_timezone = intern_local;
|
908
|
+
}
|
909
|
+
|
910
|
+
appTz = rb_hash_aref(opts, sym_application_timezone);
|
911
|
+
if (appTz == sym_local) {
|
912
|
+
app_timezone = intern_local;
|
913
|
+
} else if (appTz == sym_utc) {
|
914
|
+
app_timezone = intern_utc;
|
915
|
+
} else {
|
916
|
+
app_timezone = Qnil;
|
917
|
+
}
|
918
|
+
|
919
|
+
if (wrapper->rows == Qnil && !wrapper->is_streaming) {
|
920
|
+
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
921
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
922
|
+
} else if (wrapper->rows && !cacheRows) {
|
923
|
+
if (wrapper->resultFreed) {
|
924
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
925
|
+
}
|
926
|
+
mysql_data_seek(wrapper->result, 0);
|
927
|
+
wrapper->lastRowProcessed = 0;
|
928
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
929
|
+
}
|
930
|
+
|
931
|
+
// Backward compat
|
932
|
+
args.symbolizeKeys = symbolizeKeys;
|
933
|
+
args.asArray = asArray;
|
934
|
+
args.castBool = castBool;
|
935
|
+
args.cacheRows = cacheRows;
|
936
|
+
args.cast = cast;
|
937
|
+
args.db_timezone = db_timezone;
|
938
|
+
args.app_timezone = app_timezone;
|
939
|
+
args.block_given = block;
|
940
|
+
|
941
|
+
if (wrapper->stmt_wrapper) {
|
942
|
+
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
943
|
+
} else {
|
944
|
+
fetch_row_func = rb_mysql_result_fetch_row;
|
945
|
+
}
|
946
|
+
|
947
|
+
return rb_mysql_result_each_(self, fetch_row_func, &args);
|
948
|
+
}
|
949
|
+
|
600
950
|
static VALUE rb_mysql_result_count(VALUE self) {
|
601
|
-
|
951
|
+
GET_RESULT(self);
|
952
|
+
|
953
|
+
if (wrapper->is_streaming) {
|
954
|
+
/* This is an unsigned long per result.h */
|
955
|
+
return ULONG2NUM(wrapper->numberOfRows);
|
956
|
+
}
|
602
957
|
|
603
|
-
GetMysql2Result(self, wrapper);
|
604
958
|
if (wrapper->resultFreed) {
|
605
|
-
|
606
|
-
|
959
|
+
/* Ruby arrays have platform signed long length */
|
960
|
+
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
961
|
+
} else {
|
962
|
+
/* MySQL returns an unsigned 64-bit long here */
|
963
|
+
if (wrapper->stmt_wrapper) {
|
964
|
+
return ULL2NUM(mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt));
|
607
965
|
} else {
|
608
|
-
return
|
966
|
+
return ULL2NUM(mysql_num_rows(wrapper->result));
|
609
967
|
}
|
610
|
-
} else {
|
611
|
-
return INT2FIX(mysql_num_rows(wrapper->result));
|
612
968
|
}
|
613
969
|
}
|
614
970
|
|
615
971
|
/* Mysql2::Result */
|
616
|
-
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
972
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement) {
|
617
973
|
VALUE obj;
|
618
974
|
mysql2_result_wrapper * wrapper;
|
975
|
+
|
619
976
|
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
620
977
|
wrapper->numberOfFields = 0;
|
621
978
|
wrapper->numberOfRows = 0;
|
@@ -629,11 +986,27 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
629
986
|
wrapper->client = client;
|
630
987
|
wrapper->client_wrapper = DATA_PTR(client);
|
631
988
|
wrapper->client_wrapper->refcount++;
|
989
|
+
wrapper->result_buffers = NULL;
|
990
|
+
wrapper->is_null = NULL;
|
991
|
+
wrapper->error = NULL;
|
992
|
+
wrapper->length = NULL;
|
993
|
+
|
994
|
+
/* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
|
995
|
+
wrapper->statement = statement;
|
996
|
+
if (statement != Qnil) {
|
997
|
+
wrapper->stmt_wrapper = DATA_PTR(statement);
|
998
|
+
wrapper->stmt_wrapper->refcount++;
|
999
|
+
} else {
|
1000
|
+
wrapper->stmt_wrapper = NULL;
|
1001
|
+
}
|
632
1002
|
|
633
1003
|
rb_obj_call_init(obj, 0, NULL);
|
634
|
-
|
635
1004
|
rb_iv_set(obj, "@query_options", options);
|
636
1005
|
|
1006
|
+
/* Options that cannot be changed in results.each(...) { |row| }
|
1007
|
+
* should be processed here. */
|
1008
|
+
wrapper->is_streaming = (rb_hash_aref(options, sym_stream) == Qtrue ? 1 : 0);
|
1009
|
+
|
637
1010
|
return obj;
|
638
1011
|
}
|
639
1012
|
|
@@ -645,6 +1018,7 @@ void init_mysql2_result() {
|
|
645
1018
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
646
1019
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
647
1020
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
1021
|
+
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
648
1022
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
649
1023
|
rb_define_alias(cMysql2Result, "size", "count");
|
650
1024
|
|