mysql2 0.3.20 → 0.5.3
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 +5 -5
- data/CHANGELOG.md +1 -0
- data/README.md +183 -72
- data/ext/mysql2/client.c +432 -183
- data/ext/mysql2/client.h +10 -38
- data/ext/mysql2/extconf.rb +126 -56
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +3 -1
- data/ext/mysql2/mysql2_ext.h +13 -14
- data/ext/mysql2/mysql_enc_name_to_ruby.h +62 -58
- data/ext/mysql2/mysql_enc_to_ruby.h +82 -18
- data/ext/mysql2/result.c +505 -164
- data/ext/mysql2/result.h +11 -4
- data/ext/mysql2/statement.c +604 -0
- data/ext/mysql2/statement.h +17 -0
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +89 -28
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +6 -9
- data/lib/mysql2/error.rb +55 -35
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +11 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +46 -24
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +15 -11
- data/support/ruby_enc_to_mysql.rb +8 -6
- metadata +22 -77
- data/examples/eventmachine.rb +0 -21
- data/examples/threaded.rb +0 -20
- data/spec/configuration.yml.example +0 -17
- data/spec/em/em_spec.rb +0 -135
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -921
- data/spec/mysql2/error_spec.rb +0 -83
- data/spec/mysql2/result_spec.rb +0 -514
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -87
- data/spec/test_data +0 -1
data/ext/mysql2/result.c
CHANGED
@@ -1,68 +1,49 @@
|
|
1
1
|
#include <mysql2_ext.h>
|
2
2
|
|
3
|
-
#include <stdint.h>
|
4
|
-
|
5
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]))
|
6
5
|
|
7
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
8
6
|
static rb_encoding *binaryEncoding;
|
9
|
-
#endif
|
10
7
|
|
11
|
-
#if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
|
12
8
|
/* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
|
13
9
|
*
|
14
10
|
* (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
|
15
11
|
*/
|
16
12
|
#define MYSQL2_MAX_TIME 315578267999ULL
|
17
|
-
#else
|
18
|
-
/**
|
19
|
-
* On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
|
20
|
-
* 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds
|
21
|
-
*
|
22
|
-
* (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7
|
23
|
-
*/
|
24
|
-
#define MYSQL2_MAX_TIME 64318634047ULL
|
25
|
-
#endif
|
26
13
|
|
27
|
-
#if defined(HAVE_RUBY_ENCODING_H)
|
28
14
|
/* 0000-1-1 00:00:00 UTC
|
29
15
|
*
|
30
16
|
* (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
|
31
17
|
*/
|
32
18
|
#define MYSQL2_MIN_TIME 2678400ULL
|
33
|
-
#elif SIZEOF_INT < SIZEOF_LONG /* 64bit Ruby 1.8 */
|
34
|
-
/* 0139-1-1 00:00:00 UTC
|
35
|
-
*
|
36
|
-
* (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
|
37
|
-
*/
|
38
|
-
#define MYSQL2_MIN_TIME 4389184800ULL
|
39
|
-
#elif defined(NEGATIVE_TIME_T)
|
40
|
-
/* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t.
|
41
|
-
*
|
42
|
-
* (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52
|
43
|
-
*/
|
44
|
-
#define MYSQL2_MIN_TIME 60023299552ULL
|
45
|
-
#else
|
46
|
-
/* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t.
|
47
|
-
*
|
48
|
-
* (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1
|
49
|
-
*/
|
50
|
-
#define MYSQL2_MIN_TIME 62171150401ULL
|
51
|
-
#endif
|
52
19
|
|
53
|
-
#define GET_RESULT(
|
20
|
+
#define GET_RESULT(self) \
|
54
21
|
mysql2_result_wrapper *wrapper; \
|
55
22
|
Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
|
56
23
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
24
|
+
typedef struct {
|
25
|
+
int symbolizeKeys;
|
26
|
+
int asArray;
|
27
|
+
int castBool;
|
28
|
+
int cacheRows;
|
29
|
+
int cast;
|
30
|
+
int streaming;
|
31
|
+
ID db_timezone;
|
32
|
+
ID app_timezone;
|
33
|
+
int block_given; /* boolean */
|
34
|
+
} result_each_args;
|
65
35
|
|
36
|
+
extern VALUE mMysql2, cMysql2Client, cMysql2Error;
|
37
|
+
static VALUE cMysql2Result, cDateTime, cDate;
|
38
|
+
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
39
|
+
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
|
40
|
+
intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
|
41
|
+
intern_query_options;
|
42
|
+
static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
|
43
|
+
sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
|
44
|
+
sym_cache_rows, sym_cast, sym_stream, sym_name;
|
45
|
+
|
46
|
+
/* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
|
66
47
|
static void rb_mysql_result_mark(void * wrapper) {
|
67
48
|
mysql2_result_wrapper * w = wrapper;
|
68
49
|
if (w) {
|
@@ -70,13 +51,50 @@ static void rb_mysql_result_mark(void * wrapper) {
|
|
70
51
|
rb_gc_mark(w->rows);
|
71
52
|
rb_gc_mark(w->encoding);
|
72
53
|
rb_gc_mark(w->client);
|
54
|
+
rb_gc_mark(w->statement);
|
73
55
|
}
|
74
56
|
}
|
75
57
|
|
76
58
|
/* this may be called manually or during GC */
|
77
59
|
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
78
|
-
if (wrapper
|
60
|
+
if (!wrapper) return;
|
61
|
+
|
62
|
+
if (wrapper->resultFreed != 1) {
|
63
|
+
if (wrapper->stmt_wrapper) {
|
64
|
+
if (!wrapper->stmt_wrapper->closed) {
|
65
|
+
mysql_stmt_free_result(wrapper->stmt_wrapper->stmt);
|
66
|
+
|
67
|
+
/* MySQL BUG? If the statement handle was previously used, and so
|
68
|
+
* mysql_stmt_bind_result was called, and if that result set and bind buffers were freed,
|
69
|
+
* MySQL still thinks the result set buffer is available and will prefetch the
|
70
|
+
* first result in mysql_stmt_execute. This will corrupt or crash the program.
|
71
|
+
* By setting bind_result_done back to 0, we make MySQL think that a result set
|
72
|
+
* has never been bound to this statement handle before to prevent the prefetch.
|
73
|
+
*/
|
74
|
+
wrapper->stmt_wrapper->stmt->bind_result_done = 0;
|
75
|
+
}
|
76
|
+
|
77
|
+
if (wrapper->statement != Qnil) {
|
78
|
+
decr_mysql2_stmt(wrapper->stmt_wrapper);
|
79
|
+
}
|
80
|
+
|
81
|
+
if (wrapper->result_buffers) {
|
82
|
+
unsigned int i;
|
83
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
84
|
+
if (wrapper->result_buffers[i].buffer) {
|
85
|
+
xfree(wrapper->result_buffers[i].buffer);
|
86
|
+
}
|
87
|
+
}
|
88
|
+
xfree(wrapper->result_buffers);
|
89
|
+
xfree(wrapper->is_null);
|
90
|
+
xfree(wrapper->error);
|
91
|
+
xfree(wrapper->length);
|
92
|
+
}
|
93
|
+
/* Clue that the next statement execute will need to allocate a new result buffer. */
|
94
|
+
wrapper->result_buffers = NULL;
|
95
|
+
}
|
79
96
|
/* FIXME: this may call flush_use_result, which can hit the socket */
|
97
|
+
/* For prepared statements, wrapper->result is the result metadata */
|
80
98
|
mysql_free_result(wrapper->result);
|
81
99
|
wrapper->resultFreed = 1;
|
82
100
|
}
|
@@ -84,7 +102,7 @@ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
|
84
102
|
|
85
103
|
/* this is called during GC */
|
86
104
|
static void rb_mysql_result_free(void *ptr) {
|
87
|
-
mysql2_result_wrapper *
|
105
|
+
mysql2_result_wrapper *wrapper = ptr;
|
88
106
|
rb_mysql_result_free_result(wrapper);
|
89
107
|
|
90
108
|
// If the GC gets to client first it will be nil
|
@@ -95,6 +113,12 @@ static void rb_mysql_result_free(void *ptr) {
|
|
95
113
|
xfree(wrapper);
|
96
114
|
}
|
97
115
|
|
116
|
+
static VALUE rb_mysql_result_free_(VALUE self) {
|
117
|
+
GET_RESULT(self);
|
118
|
+
rb_mysql_result_free_result(wrapper);
|
119
|
+
return Qnil;
|
120
|
+
}
|
121
|
+
|
98
122
|
/*
|
99
123
|
* for small results, this won't hit the network, but there's no
|
100
124
|
* reliable way for us to tell this so we'll always release the GVL
|
@@ -106,7 +130,14 @@ static void *nogvl_fetch_row(void *ptr) {
|
|
106
130
|
return mysql_fetch_row(result);
|
107
131
|
}
|
108
132
|
|
109
|
-
static
|
133
|
+
static void *nogvl_stmt_fetch(void *ptr) {
|
134
|
+
MYSQL_STMT *stmt = ptr;
|
135
|
+
uintptr_t r = mysql_stmt_fetch(stmt);
|
136
|
+
|
137
|
+
return (void *)r;
|
138
|
+
}
|
139
|
+
|
140
|
+
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
|
110
141
|
VALUE rb_field;
|
111
142
|
GET_RESULT(self);
|
112
143
|
|
@@ -118,29 +149,19 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
118
149
|
rb_field = rb_ary_entry(wrapper->fields, idx);
|
119
150
|
if (rb_field == Qnil) {
|
120
151
|
MYSQL_FIELD *field = NULL;
|
121
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
122
152
|
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
123
153
|
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
124
|
-
#endif
|
125
154
|
|
126
155
|
field = mysql_fetch_field_direct(wrapper->result, idx);
|
127
156
|
if (symbolize_keys) {
|
128
|
-
#ifdef HAVE_RB_INTERN3
|
129
157
|
rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
|
130
158
|
rb_field = ID2SYM(rb_field);
|
131
|
-
#else
|
132
|
-
VALUE colStr;
|
133
|
-
colStr = rb_str_new(field->name, field->name_length);
|
134
|
-
rb_field = ID2SYM(rb_to_id(colStr));
|
135
|
-
#endif
|
136
159
|
} else {
|
137
160
|
rb_field = rb_str_new(field->name, field->name_length);
|
138
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
139
161
|
rb_enc_associate(rb_field, conn_enc);
|
140
162
|
if (default_internal_enc) {
|
141
163
|
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
142
164
|
}
|
143
|
-
#endif
|
144
165
|
}
|
145
166
|
rb_ary_store(wrapper->fields, idx, rb_field);
|
146
167
|
}
|
@@ -148,9 +169,8 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
|
|
148
169
|
return rb_field;
|
149
170
|
}
|
150
171
|
|
151
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
152
172
|
static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_encoding *default_internal_enc, rb_encoding *conn_enc) {
|
153
|
-
/* if binary flag is set, respect
|
173
|
+
/* if binary flag is set, respect its wishes */
|
154
174
|
if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
|
155
175
|
rb_enc_associate(val, binaryEncoding);
|
156
176
|
} else if (!field.charsetnr) {
|
@@ -161,7 +181,8 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
161
181
|
const char *enc_name;
|
162
182
|
int enc_index;
|
163
183
|
|
164
|
-
enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
|
184
|
+
enc_name = (field.charsetnr-1 < MYSQL2_CHARSETNR_SIZE) ? mysql2_mysql_enc_to_rb[field.charsetnr-1] : NULL;
|
185
|
+
|
165
186
|
if (enc_name != NULL) {
|
166
187
|
/* use the field encoding we were able to match */
|
167
188
|
enc_index = rb_enc_find_index(enc_name);
|
@@ -177,7 +198,6 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
177
198
|
}
|
178
199
|
return val;
|
179
200
|
}
|
180
|
-
#endif
|
181
201
|
|
182
202
|
/* Interpret microseconds digits left-aligned in fixed-width field.
|
183
203
|
* e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
|
@@ -185,7 +205,7 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
185
205
|
*/
|
186
206
|
static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
187
207
|
{
|
188
|
-
|
208
|
+
size_t i;
|
189
209
|
for (i = 0; i < (len - 1); i++) {
|
190
210
|
if (msec_char[i] == '\0') {
|
191
211
|
msec_char[i] = '0';
|
@@ -194,22 +214,281 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
194
214
|
return (unsigned int)strtoul(msec_char, NULL, 10);
|
195
215
|
}
|
196
216
|
|
197
|
-
static
|
217
|
+
static void rb_mysql_result_alloc_result_buffers(VALUE self, MYSQL_FIELD *fields) {
|
218
|
+
unsigned int i;
|
219
|
+
GET_RESULT(self);
|
220
|
+
|
221
|
+
if (wrapper->result_buffers != NULL) return;
|
222
|
+
|
223
|
+
wrapper->result_buffers = xcalloc(wrapper->numberOfFields, sizeof(MYSQL_BIND));
|
224
|
+
wrapper->is_null = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
225
|
+
wrapper->error = xcalloc(wrapper->numberOfFields, sizeof(my_bool));
|
226
|
+
wrapper->length = xcalloc(wrapper->numberOfFields, sizeof(unsigned long));
|
227
|
+
|
228
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
229
|
+
wrapper->result_buffers[i].buffer_type = fields[i].type;
|
230
|
+
|
231
|
+
// mysql type | C type
|
232
|
+
switch(fields[i].type) {
|
233
|
+
case MYSQL_TYPE_NULL: // NULL
|
234
|
+
break;
|
235
|
+
case MYSQL_TYPE_TINY: // signed char
|
236
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(signed char));
|
237
|
+
wrapper->result_buffers[i].buffer_length = sizeof(signed char);
|
238
|
+
break;
|
239
|
+
case MYSQL_TYPE_SHORT: // short int
|
240
|
+
case MYSQL_TYPE_YEAR: // short int
|
241
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(short int));
|
242
|
+
wrapper->result_buffers[i].buffer_length = sizeof(short int);
|
243
|
+
break;
|
244
|
+
case MYSQL_TYPE_INT24: // int
|
245
|
+
case MYSQL_TYPE_LONG: // int
|
246
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(int));
|
247
|
+
wrapper->result_buffers[i].buffer_length = sizeof(int);
|
248
|
+
break;
|
249
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
250
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(long long int));
|
251
|
+
wrapper->result_buffers[i].buffer_length = sizeof(long long int);
|
252
|
+
break;
|
253
|
+
case MYSQL_TYPE_FLOAT: // float
|
254
|
+
case MYSQL_TYPE_DOUBLE: // double
|
255
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(double));
|
256
|
+
wrapper->result_buffers[i].buffer_length = sizeof(double);
|
257
|
+
break;
|
258
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
259
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
260
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
261
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
262
|
+
case MYSQL_TYPE_TIMESTAMP: // MYSQL_TIME
|
263
|
+
wrapper->result_buffers[i].buffer = xcalloc(1, sizeof(MYSQL_TIME));
|
264
|
+
wrapper->result_buffers[i].buffer_length = sizeof(MYSQL_TIME);
|
265
|
+
break;
|
266
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
267
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
268
|
+
case MYSQL_TYPE_STRING: // char[]
|
269
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
270
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
271
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
272
|
+
case MYSQL_TYPE_BLOB: // char[]
|
273
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
274
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
275
|
+
case MYSQL_TYPE_BIT: // char[]
|
276
|
+
case MYSQL_TYPE_SET: // char[]
|
277
|
+
case MYSQL_TYPE_ENUM: // char[]
|
278
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
279
|
+
default:
|
280
|
+
wrapper->result_buffers[i].buffer = xmalloc(fields[i].max_length);
|
281
|
+
wrapper->result_buffers[i].buffer_length = fields[i].max_length;
|
282
|
+
break;
|
283
|
+
}
|
284
|
+
|
285
|
+
wrapper->result_buffers[i].is_null = &wrapper->is_null[i];
|
286
|
+
wrapper->result_buffers[i].length = &wrapper->length[i];
|
287
|
+
wrapper->result_buffers[i].error = &wrapper->error[i];
|
288
|
+
wrapper->result_buffers[i].is_unsigned = ((fields[i].flags & UNSIGNED_FLAG) != 0);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
|
292
|
+
static VALUE rb_mysql_result_fetch_row_stmt(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
293
|
+
{
|
294
|
+
VALUE rowVal;
|
295
|
+
unsigned int i = 0;
|
296
|
+
|
297
|
+
rb_encoding *default_internal_enc;
|
298
|
+
rb_encoding *conn_enc;
|
299
|
+
GET_RESULT(self);
|
300
|
+
|
301
|
+
default_internal_enc = rb_default_internal_encoding();
|
302
|
+
conn_enc = rb_to_encoding(wrapper->encoding);
|
303
|
+
|
304
|
+
if (wrapper->fields == Qnil) {
|
305
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
306
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
307
|
+
}
|
308
|
+
if (args->asArray) {
|
309
|
+
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
310
|
+
} else {
|
311
|
+
rowVal = rb_hash_new();
|
312
|
+
}
|
313
|
+
|
314
|
+
if (wrapper->result_buffers == NULL) {
|
315
|
+
rb_mysql_result_alloc_result_buffers(self, fields);
|
316
|
+
}
|
317
|
+
|
318
|
+
if (mysql_stmt_bind_result(wrapper->stmt_wrapper->stmt, wrapper->result_buffers)) {
|
319
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
320
|
+
}
|
321
|
+
|
322
|
+
{
|
323
|
+
switch((uintptr_t)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper->stmt_wrapper->stmt, RUBY_UBF_IO, 0)) {
|
324
|
+
case 0:
|
325
|
+
/* success */
|
326
|
+
break;
|
327
|
+
|
328
|
+
case 1:
|
329
|
+
/* error */
|
330
|
+
rb_raise_mysql2_stmt_error(wrapper->stmt_wrapper);
|
331
|
+
|
332
|
+
case MYSQL_NO_DATA:
|
333
|
+
/* no more row */
|
334
|
+
return Qnil;
|
335
|
+
|
336
|
+
case MYSQL_DATA_TRUNCATED:
|
337
|
+
rb_raise(cMysql2Error, "IMPLBUG: caught MYSQL_DATA_TRUNCATED. should not come here as buffer_length is set to fields[i].max_length.");
|
338
|
+
}
|
339
|
+
}
|
340
|
+
|
341
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
342
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
343
|
+
VALUE val = Qnil;
|
344
|
+
MYSQL_TIME *ts;
|
345
|
+
|
346
|
+
if (wrapper->is_null[i]) {
|
347
|
+
val = Qnil;
|
348
|
+
} else {
|
349
|
+
const MYSQL_BIND* const result_buffer = &wrapper->result_buffers[i];
|
350
|
+
|
351
|
+
switch(result_buffer->buffer_type) {
|
352
|
+
case MYSQL_TYPE_TINY: // signed char
|
353
|
+
if (args->castBool && fields[i].length == 1) {
|
354
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
355
|
+
break;
|
356
|
+
}
|
357
|
+
if (result_buffer->is_unsigned) {
|
358
|
+
val = UINT2NUM(*((unsigned char*)result_buffer->buffer));
|
359
|
+
} else {
|
360
|
+
val = INT2NUM(*((signed char*)result_buffer->buffer));
|
361
|
+
}
|
362
|
+
break;
|
363
|
+
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
364
|
+
if (args->castBool && fields[i].length == 1) {
|
365
|
+
val = (*((unsigned char*)result_buffer->buffer) != 0) ? Qtrue : Qfalse;
|
366
|
+
}else{
|
367
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
368
|
+
}
|
369
|
+
break;
|
370
|
+
case MYSQL_TYPE_SHORT: // short int
|
371
|
+
case MYSQL_TYPE_YEAR: // short int
|
372
|
+
if (result_buffer->is_unsigned) {
|
373
|
+
val = UINT2NUM(*((unsigned short int*)result_buffer->buffer));
|
374
|
+
} else {
|
375
|
+
val = INT2NUM(*((short int*)result_buffer->buffer));
|
376
|
+
}
|
377
|
+
break;
|
378
|
+
case MYSQL_TYPE_INT24: // int
|
379
|
+
case MYSQL_TYPE_LONG: // int
|
380
|
+
if (result_buffer->is_unsigned) {
|
381
|
+
val = UINT2NUM(*((unsigned int*)result_buffer->buffer));
|
382
|
+
} else {
|
383
|
+
val = INT2NUM(*((int*)result_buffer->buffer));
|
384
|
+
}
|
385
|
+
break;
|
386
|
+
case MYSQL_TYPE_LONGLONG: // long long int
|
387
|
+
if (result_buffer->is_unsigned) {
|
388
|
+
val = ULL2NUM(*((unsigned long long int*)result_buffer->buffer));
|
389
|
+
} else {
|
390
|
+
val = LL2NUM(*((long long int*)result_buffer->buffer));
|
391
|
+
}
|
392
|
+
break;
|
393
|
+
case MYSQL_TYPE_FLOAT: // float
|
394
|
+
val = rb_float_new((double)(*((float*)result_buffer->buffer)));
|
395
|
+
break;
|
396
|
+
case MYSQL_TYPE_DOUBLE: // double
|
397
|
+
val = rb_float_new((double)(*((double*)result_buffer->buffer)));
|
398
|
+
break;
|
399
|
+
case MYSQL_TYPE_DATE: // MYSQL_TIME
|
400
|
+
case MYSQL_TYPE_NEWDATE: // MYSQL_TIME
|
401
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
402
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(ts->year), INT2NUM(ts->month), INT2NUM(ts->day));
|
403
|
+
break;
|
404
|
+
case MYSQL_TYPE_TIME: // MYSQL_TIME
|
405
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
406
|
+
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));
|
407
|
+
if (!NIL_P(args->app_timezone)) {
|
408
|
+
if (args->app_timezone == intern_local) {
|
409
|
+
val = rb_funcall(val, intern_localtime, 0);
|
410
|
+
} else { // utc
|
411
|
+
val = rb_funcall(val, intern_utc, 0);
|
412
|
+
}
|
413
|
+
}
|
414
|
+
break;
|
415
|
+
case MYSQL_TYPE_DATETIME: // MYSQL_TIME
|
416
|
+
case MYSQL_TYPE_TIMESTAMP: { // MYSQL_TIME
|
417
|
+
uint64_t seconds;
|
418
|
+
|
419
|
+
ts = (MYSQL_TIME*)result_buffer->buffer;
|
420
|
+
seconds = (ts->year*31557600ULL) + (ts->month*2592000ULL) + (ts->day*86400ULL) + (ts->hour*3600ULL) + (ts->minute*60ULL) + ts->second;
|
421
|
+
|
422
|
+
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
|
423
|
+
VALUE offset = INT2NUM(0);
|
424
|
+
if (args->db_timezone == intern_local) {
|
425
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
426
|
+
}
|
427
|
+
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);
|
428
|
+
if (!NIL_P(args->app_timezone)) {
|
429
|
+
if (args->app_timezone == intern_local) {
|
430
|
+
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
431
|
+
val = rb_funcall(val, intern_new_offset, 1, offset);
|
432
|
+
} else { // utc
|
433
|
+
val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
|
434
|
+
}
|
435
|
+
}
|
436
|
+
} else {
|
437
|
+
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));
|
438
|
+
if (!NIL_P(args->app_timezone)) {
|
439
|
+
if (args->app_timezone == intern_local) {
|
440
|
+
val = rb_funcall(val, intern_localtime, 0);
|
441
|
+
} else { // utc
|
442
|
+
val = rb_funcall(val, intern_utc, 0);
|
443
|
+
}
|
444
|
+
}
|
445
|
+
}
|
446
|
+
break;
|
447
|
+
}
|
448
|
+
case MYSQL_TYPE_DECIMAL: // char[]
|
449
|
+
case MYSQL_TYPE_NEWDECIMAL: // char[]
|
450
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(result_buffer->buffer, *(result_buffer->length)));
|
451
|
+
break;
|
452
|
+
case MYSQL_TYPE_STRING: // char[]
|
453
|
+
case MYSQL_TYPE_VAR_STRING: // char[]
|
454
|
+
case MYSQL_TYPE_VARCHAR: // char[]
|
455
|
+
case MYSQL_TYPE_TINY_BLOB: // char[]
|
456
|
+
case MYSQL_TYPE_BLOB: // char[]
|
457
|
+
case MYSQL_TYPE_MEDIUM_BLOB: // char[]
|
458
|
+
case MYSQL_TYPE_LONG_BLOB: // char[]
|
459
|
+
case MYSQL_TYPE_SET: // char[]
|
460
|
+
case MYSQL_TYPE_ENUM: // char[]
|
461
|
+
case MYSQL_TYPE_GEOMETRY: // char[]
|
462
|
+
default:
|
463
|
+
val = rb_str_new(result_buffer->buffer, *(result_buffer->length));
|
464
|
+
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
465
|
+
break;
|
466
|
+
}
|
467
|
+
}
|
468
|
+
|
469
|
+
if (args->asArray) {
|
470
|
+
rb_ary_push(rowVal, val);
|
471
|
+
} else {
|
472
|
+
rb_hash_aset(rowVal, field, val);
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
return rowVal;
|
477
|
+
}
|
478
|
+
|
479
|
+
static VALUE rb_mysql_result_fetch_row(VALUE self, MYSQL_FIELD * fields, const result_each_args *args)
|
480
|
+
{
|
198
481
|
VALUE rowVal;
|
199
482
|
MYSQL_ROW row;
|
200
483
|
unsigned int i = 0;
|
201
484
|
unsigned long * fieldLengths;
|
202
485
|
void * ptr;
|
203
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
204
486
|
rb_encoding *default_internal_enc;
|
205
487
|
rb_encoding *conn_enc;
|
206
|
-
#endif
|
207
488
|
GET_RESULT(self);
|
208
489
|
|
209
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
210
490
|
default_internal_enc = rb_default_internal_encoding();
|
211
491
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
212
|
-
#endif
|
213
492
|
|
214
493
|
ptr = wrapper->result;
|
215
494
|
row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
|
@@ -217,31 +496,29 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
217
496
|
return Qnil;
|
218
497
|
}
|
219
498
|
|
220
|
-
if (
|
499
|
+
if (wrapper->fields == Qnil) {
|
500
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
501
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
502
|
+
}
|
503
|
+
if (args->asArray) {
|
221
504
|
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
222
505
|
} else {
|
223
506
|
rowVal = rb_hash_new();
|
224
507
|
}
|
225
508
|
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
226
|
-
if (wrapper->fields == Qnil) {
|
227
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
228
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
229
|
-
}
|
230
509
|
|
231
510
|
for (i = 0; i < wrapper->numberOfFields; i++) {
|
232
|
-
VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
511
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, args->symbolizeKeys);
|
233
512
|
if (row[i]) {
|
234
513
|
VALUE val = Qnil;
|
235
514
|
enum enum_field_types type = fields[i].type;
|
236
515
|
|
237
|
-
if (!cast) {
|
516
|
+
if (!args->cast) {
|
238
517
|
if (type == MYSQL_TYPE_NULL) {
|
239
518
|
val = Qnil;
|
240
519
|
} else {
|
241
520
|
val = rb_str_new(row[i], fieldLengths[i]);
|
242
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
243
521
|
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
244
|
-
#endif
|
245
522
|
}
|
246
523
|
} else {
|
247
524
|
switch(type) {
|
@@ -249,14 +526,14 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
249
526
|
val = Qnil;
|
250
527
|
break;
|
251
528
|
case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
|
252
|
-
if (castBool && fields[i].length == 1) {
|
529
|
+
if (args->castBool && fields[i].length == 1) {
|
253
530
|
val = *row[i] == 1 ? Qtrue : Qfalse;
|
254
531
|
}else{
|
255
532
|
val = rb_str_new(row[i], fieldLengths[i]);
|
256
533
|
}
|
257
534
|
break;
|
258
535
|
case MYSQL_TYPE_TINY: /* TINYINT field */
|
259
|
-
if (castBool && fields[i].length == 1) {
|
536
|
+
if (args->castBool && fields[i].length == 1) {
|
260
537
|
val = *row[i] != '0' ? Qtrue : Qfalse;
|
261
538
|
break;
|
262
539
|
}
|
@@ -272,9 +549,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
272
549
|
if (fields[i].decimals == 0) {
|
273
550
|
val = rb_cstr2inum(row[i], 10);
|
274
551
|
} else if (strtod(row[i], NULL) == 0.000000){
|
275
|
-
val = rb_funcall(
|
552
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
|
276
553
|
}else{
|
277
|
-
val = rb_funcall(
|
554
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
|
278
555
|
}
|
279
556
|
break;
|
280
557
|
case MYSQL_TYPE_FLOAT: /* FLOAT field */
|
@@ -299,9 +576,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
299
576
|
break;
|
300
577
|
}
|
301
578
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
302
|
-
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));
|
303
|
-
if (!NIL_P(app_timezone)) {
|
304
|
-
if (app_timezone == intern_local) {
|
579
|
+
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));
|
580
|
+
if (!NIL_P(args->app_timezone)) {
|
581
|
+
if (args->app_timezone == intern_local) {
|
305
582
|
val = rb_funcall(val, intern_localtime, 0);
|
306
583
|
} else { /* utc */
|
307
584
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -332,12 +609,12 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
332
609
|
} else {
|
333
610
|
if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
|
334
611
|
VALUE offset = INT2NUM(0);
|
335
|
-
if (db_timezone == intern_local) {
|
612
|
+
if (args->db_timezone == intern_local) {
|
336
613
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
337
614
|
}
|
338
615
|
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), offset);
|
339
|
-
if (!NIL_P(app_timezone)) {
|
340
|
-
if (app_timezone == intern_local) {
|
616
|
+
if (!NIL_P(args->app_timezone)) {
|
617
|
+
if (args->app_timezone == intern_local) {
|
341
618
|
offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
|
342
619
|
val = rb_funcall(val, intern_new_offset, 1, offset);
|
343
620
|
} else { /* utc */
|
@@ -346,9 +623,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
346
623
|
}
|
347
624
|
} else {
|
348
625
|
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
349
|
-
val = rb_funcall(rb_cTime, db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
350
|
-
if (!NIL_P(app_timezone)) {
|
351
|
-
if (app_timezone == intern_local) {
|
626
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
627
|
+
if (!NIL_P(args->app_timezone)) {
|
628
|
+
if (args->app_timezone == intern_local) {
|
352
629
|
val = rb_funcall(val, intern_localtime, 0);
|
353
630
|
} else { /* utc */
|
354
631
|
val = rb_funcall(val, intern_utc, 0);
|
@@ -392,19 +669,17 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
392
669
|
case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
|
393
670
|
default:
|
394
671
|
val = rb_str_new(row[i], fieldLengths[i]);
|
395
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
396
672
|
val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
|
397
|
-
#endif
|
398
673
|
break;
|
399
674
|
}
|
400
675
|
}
|
401
|
-
if (asArray) {
|
676
|
+
if (args->asArray) {
|
402
677
|
rb_ary_push(rowVal, val);
|
403
678
|
} else {
|
404
679
|
rb_hash_aset(rowVal, field, val);
|
405
680
|
}
|
406
681
|
} else {
|
407
|
-
if (asArray) {
|
682
|
+
if (args->asArray) {
|
408
683
|
rb_ary_push(rowVal, Qnil);
|
409
684
|
} else {
|
410
685
|
rb_hash_aset(rowVal, field, Qnil);
|
@@ -421,7 +696,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
421
696
|
|
422
697
|
GET_RESULT(self);
|
423
698
|
|
424
|
-
defaults =
|
699
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
425
700
|
Check_Type(defaults, T_HASH);
|
426
701
|
if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
|
427
702
|
symbolizeKeys = 1;
|
@@ -432,7 +707,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
432
707
|
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
433
708
|
}
|
434
709
|
|
435
|
-
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
710
|
+
if ((my_ulonglong)RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
436
711
|
for (i=0; i<wrapper->numberOfFields; i++) {
|
437
712
|
rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
438
713
|
}
|
@@ -441,55 +716,16 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
441
716
|
return wrapper->fields;
|
442
717
|
}
|
443
718
|
|
444
|
-
static VALUE
|
445
|
-
|
446
|
-
|
719
|
+
static VALUE rb_mysql_result_each_(VALUE self,
|
720
|
+
VALUE(*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args),
|
721
|
+
const result_each_args *args)
|
722
|
+
{
|
447
723
|
unsigned long i;
|
448
|
-
const char *
|
449
|
-
|
450
|
-
MYSQL_FIELD * fields = NULL;
|
724
|
+
const char *errstr;
|
725
|
+
MYSQL_FIELD *fields = NULL;
|
451
726
|
|
452
727
|
GET_RESULT(self);
|
453
728
|
|
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
|
-
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
463
|
-
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
464
|
-
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
465
|
-
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
466
|
-
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
467
|
-
|
468
|
-
if (wrapper->is_streaming && cacheRows) {
|
469
|
-
rb_warn(":cache_rows is ignored if :stream is true");
|
470
|
-
}
|
471
|
-
|
472
|
-
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
473
|
-
if (dbTz == sym_local) {
|
474
|
-
db_timezone = intern_local;
|
475
|
-
} else if (dbTz == sym_utc) {
|
476
|
-
db_timezone = intern_utc;
|
477
|
-
} else {
|
478
|
-
if (!NIL_P(dbTz)) {
|
479
|
-
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
480
|
-
}
|
481
|
-
db_timezone = intern_local;
|
482
|
-
}
|
483
|
-
|
484
|
-
appTz = rb_hash_aref(opts, sym_application_timezone);
|
485
|
-
if (appTz == sym_local) {
|
486
|
-
app_timezone = intern_local;
|
487
|
-
} else if (appTz == sym_utc) {
|
488
|
-
app_timezone = intern_utc;
|
489
|
-
} else {
|
490
|
-
app_timezone = Qnil;
|
491
|
-
}
|
492
|
-
|
493
729
|
if (wrapper->is_streaming) {
|
494
730
|
/* When streaming, we will only yield rows, not return them. */
|
495
731
|
if (wrapper->rows == Qnil) {
|
@@ -502,10 +738,10 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
502
738
|
fields = mysql_fetch_fields(wrapper->result);
|
503
739
|
|
504
740
|
do {
|
505
|
-
row =
|
741
|
+
row = fetch_row_func(self, fields, args);
|
506
742
|
if (row != Qnil) {
|
507
743
|
wrapper->numberOfRows++;
|
508
|
-
if (
|
744
|
+
if (args->block_given) {
|
509
745
|
rb_yield(row);
|
510
746
|
}
|
511
747
|
}
|
@@ -524,16 +760,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
524
760
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
525
761
|
}
|
526
762
|
} else {
|
527
|
-
if (wrapper->lastRowProcessed ==
|
528
|
-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
529
|
-
if (wrapper->numberOfRows == 0) {
|
530
|
-
wrapper->rows = rb_ary_new();
|
531
|
-
return wrapper->rows;
|
532
|
-
}
|
533
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
534
|
-
}
|
535
|
-
|
536
|
-
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
763
|
+
if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
537
764
|
/* we've already read the entire dataset from the C result into our */
|
538
765
|
/* internal array. Lets hand that over to the user since it's ready to go */
|
539
766
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
@@ -546,11 +773,11 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
546
773
|
|
547
774
|
for (i = 0; i < wrapper->numberOfRows; i++) {
|
548
775
|
VALUE row;
|
549
|
-
if (cacheRows && i < rowsProcessed) {
|
776
|
+
if (args->cacheRows && i < rowsProcessed) {
|
550
777
|
row = rb_ary_entry(wrapper->rows, i);
|
551
778
|
} else {
|
552
|
-
row =
|
553
|
-
if (cacheRows) {
|
779
|
+
row = fetch_row_func(self, fields, args);
|
780
|
+
if (args->cacheRows) {
|
554
781
|
rb_ary_store(wrapper->rows, i, row);
|
555
782
|
}
|
556
783
|
wrapper->lastRowProcessed++;
|
@@ -558,24 +785,122 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
558
785
|
|
559
786
|
if (row == Qnil) {
|
560
787
|
/* we don't need the mysql C dataset around anymore, peace it */
|
561
|
-
|
788
|
+
if (args->cacheRows) {
|
789
|
+
rb_mysql_result_free_result(wrapper);
|
790
|
+
}
|
562
791
|
return Qnil;
|
563
792
|
}
|
564
793
|
|
565
|
-
if (
|
794
|
+
if (args->block_given) {
|
566
795
|
rb_yield(row);
|
567
796
|
}
|
568
797
|
}
|
569
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
798
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows && args->cacheRows) {
|
570
799
|
/* we don't need the mysql C dataset around anymore, peace it */
|
571
800
|
rb_mysql_result_free_result(wrapper);
|
572
801
|
}
|
573
802
|
}
|
574
803
|
}
|
575
804
|
|
805
|
+
// FIXME return Enumerator instead?
|
806
|
+
// return rb_ary_each(wrapper->rows);
|
576
807
|
return wrapper->rows;
|
577
808
|
}
|
578
809
|
|
810
|
+
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
811
|
+
result_each_args args;
|
812
|
+
VALUE defaults, opts, (*fetch_row_func)(VALUE, MYSQL_FIELD *fields, const result_each_args *args);
|
813
|
+
ID db_timezone, app_timezone, dbTz, appTz;
|
814
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
815
|
+
|
816
|
+
GET_RESULT(self);
|
817
|
+
|
818
|
+
if (wrapper->stmt_wrapper && wrapper->stmt_wrapper->closed) {
|
819
|
+
rb_raise(cMysql2Error, "Statement handle already closed");
|
820
|
+
}
|
821
|
+
|
822
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
823
|
+
Check_Type(defaults, T_HASH);
|
824
|
+
|
825
|
+
// A block can be passed to this method, but since we don't call the block directly from C,
|
826
|
+
// we don't need to capture it into a variable here with the "&" scan arg.
|
827
|
+
if (rb_scan_args(argc, argv, "01", &opts) == 1) {
|
828
|
+
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
829
|
+
} else {
|
830
|
+
opts = defaults;
|
831
|
+
}
|
832
|
+
|
833
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
834
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
835
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
836
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
837
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
838
|
+
|
839
|
+
if (wrapper->is_streaming && cacheRows) {
|
840
|
+
rb_warn(":cache_rows is ignored if :stream is true");
|
841
|
+
}
|
842
|
+
|
843
|
+
if (wrapper->stmt_wrapper && !cacheRows && !wrapper->is_streaming) {
|
844
|
+
rb_warn(":cache_rows is forced for prepared statements (if not streaming)");
|
845
|
+
cacheRows = 1;
|
846
|
+
}
|
847
|
+
|
848
|
+
if (wrapper->stmt_wrapper && !cast) {
|
849
|
+
rb_warn(":cast is forced for prepared statements");
|
850
|
+
}
|
851
|
+
|
852
|
+
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
853
|
+
if (dbTz == sym_local) {
|
854
|
+
db_timezone = intern_local;
|
855
|
+
} else if (dbTz == sym_utc) {
|
856
|
+
db_timezone = intern_utc;
|
857
|
+
} else {
|
858
|
+
if (!NIL_P(dbTz)) {
|
859
|
+
rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
|
860
|
+
}
|
861
|
+
db_timezone = intern_local;
|
862
|
+
}
|
863
|
+
|
864
|
+
appTz = rb_hash_aref(opts, sym_application_timezone);
|
865
|
+
if (appTz == sym_local) {
|
866
|
+
app_timezone = intern_local;
|
867
|
+
} else if (appTz == sym_utc) {
|
868
|
+
app_timezone = intern_utc;
|
869
|
+
} else {
|
870
|
+
app_timezone = Qnil;
|
871
|
+
}
|
872
|
+
|
873
|
+
if (wrapper->rows == Qnil && !wrapper->is_streaming) {
|
874
|
+
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
875
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
876
|
+
} else if (wrapper->rows && !cacheRows) {
|
877
|
+
if (wrapper->resultFreed) {
|
878
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
879
|
+
}
|
880
|
+
mysql_data_seek(wrapper->result, 0);
|
881
|
+
wrapper->lastRowProcessed = 0;
|
882
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
883
|
+
}
|
884
|
+
|
885
|
+
// Backward compat
|
886
|
+
args.symbolizeKeys = symbolizeKeys;
|
887
|
+
args.asArray = asArray;
|
888
|
+
args.castBool = castBool;
|
889
|
+
args.cacheRows = cacheRows;
|
890
|
+
args.cast = cast;
|
891
|
+
args.db_timezone = db_timezone;
|
892
|
+
args.app_timezone = app_timezone;
|
893
|
+
args.block_given = rb_block_given_p();
|
894
|
+
|
895
|
+
if (wrapper->stmt_wrapper) {
|
896
|
+
fetch_row_func = rb_mysql_result_fetch_row_stmt;
|
897
|
+
} else {
|
898
|
+
fetch_row_func = rb_mysql_result_fetch_row;
|
899
|
+
}
|
900
|
+
|
901
|
+
return rb_mysql_result_each_(self, fetch_row_func, &args);
|
902
|
+
}
|
903
|
+
|
579
904
|
static VALUE rb_mysql_result_count(VALUE self) {
|
580
905
|
GET_RESULT(self);
|
581
906
|
|
@@ -589,12 +914,16 @@ static VALUE rb_mysql_result_count(VALUE self) {
|
|
589
914
|
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
590
915
|
} else {
|
591
916
|
/* MySQL returns an unsigned 64-bit long here */
|
592
|
-
|
917
|
+
if (wrapper->stmt_wrapper) {
|
918
|
+
return ULL2NUM(mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt));
|
919
|
+
} else {
|
920
|
+
return ULL2NUM(mysql_num_rows(wrapper->result));
|
921
|
+
}
|
593
922
|
}
|
594
923
|
}
|
595
924
|
|
596
925
|
/* Mysql2::Result */
|
597
|
-
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
926
|
+
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r, VALUE statement) {
|
598
927
|
VALUE obj;
|
599
928
|
mysql2_result_wrapper * wrapper;
|
600
929
|
|
@@ -611,10 +940,22 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
611
940
|
wrapper->client = client;
|
612
941
|
wrapper->client_wrapper = DATA_PTR(client);
|
613
942
|
wrapper->client_wrapper->refcount++;
|
943
|
+
wrapper->result_buffers = NULL;
|
944
|
+
wrapper->is_null = NULL;
|
945
|
+
wrapper->error = NULL;
|
946
|
+
wrapper->length = NULL;
|
947
|
+
|
948
|
+
/* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
|
949
|
+
wrapper->statement = statement;
|
950
|
+
if (statement != Qnil) {
|
951
|
+
wrapper->stmt_wrapper = DATA_PTR(statement);
|
952
|
+
wrapper->stmt_wrapper->refcount++;
|
953
|
+
} else {
|
954
|
+
wrapper->stmt_wrapper = NULL;
|
955
|
+
}
|
614
956
|
|
615
957
|
rb_obj_call_init(obj, 0, NULL);
|
616
|
-
|
617
|
-
rb_iv_set(obj, "@query_options", options);
|
958
|
+
rb_ivar_set(obj, intern_query_options, options);
|
618
959
|
|
619
960
|
/* Options that cannot be changed in results.each(...) { |row| }
|
620
961
|
* should be processed here. */
|
@@ -624,13 +965,13 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
624
965
|
}
|
625
966
|
|
626
967
|
void init_mysql2_result() {
|
627
|
-
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
628
968
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
629
969
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
630
970
|
|
631
971
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
632
972
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
633
973
|
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
974
|
+
rb_define_method(cMysql2Result, "free", rb_mysql_result_free_, 0);
|
634
975
|
rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
|
635
976
|
rb_define_alias(cMysql2Result, "size", "count");
|
636
977
|
|
@@ -642,6 +983,8 @@ void init_mysql2_result() {
|
|
642
983
|
intern_local_offset = rb_intern("local_offset");
|
643
984
|
intern_civil = rb_intern("civil");
|
644
985
|
intern_new_offset = rb_intern("new_offset");
|
986
|
+
intern_BigDecimal = rb_intern("BigDecimal");
|
987
|
+
intern_query_options = rb_intern("@query_options");
|
645
988
|
|
646
989
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
647
990
|
sym_as = ID2SYM(rb_intern("as"));
|
@@ -664,7 +1007,5 @@ void init_mysql2_result() {
|
|
664
1007
|
opt_time_month = INT2NUM(1);
|
665
1008
|
opt_utc_offset = INT2NUM(0);
|
666
1009
|
|
667
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
668
1010
|
binaryEncoding = rb_enc_find("binary");
|
669
|
-
#endif
|
670
1011
|
}
|