mysql2 0.3.18-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,246 @@
1
+ const char *mysql2_mysql_enc_to_rb[] = {
2
+ "Big5",
3
+ "ISO-8859-2",
4
+ NULL,
5
+ "CP850",
6
+ "ISO-8859-1",
7
+ NULL,
8
+ "KOI8-R",
9
+ "ISO-8859-1",
10
+ "ISO-8859-2",
11
+ NULL,
12
+ "US-ASCII",
13
+ "eucJP-ms",
14
+ "Shift_JIS",
15
+ "Windows-1251",
16
+ "ISO-8859-1",
17
+ "ISO-8859-8",
18
+ NULL,
19
+ "TIS-620",
20
+ "EUC-KR",
21
+ "ISO-8859-13",
22
+ "ISO-8859-2",
23
+ "KOI8-R",
24
+ "Windows-1251",
25
+ "GB2312",
26
+ "ISO-8859-7",
27
+ "Windows-1250",
28
+ "ISO-8859-2",
29
+ "GBK",
30
+ "Windows-1257",
31
+ "ISO-8859-9",
32
+ "ISO-8859-1",
33
+ NULL,
34
+ "UTF-8",
35
+ "Windows-1250",
36
+ "UTF-16BE",
37
+ "IBM866",
38
+ NULL,
39
+ "macCentEuro",
40
+ "macRoman",
41
+ "CP852",
42
+ "ISO-8859-13",
43
+ "ISO-8859-13",
44
+ "macCentEuro",
45
+ "Windows-1250",
46
+ "UTF-8",
47
+ "UTF-8",
48
+ "ISO-8859-1",
49
+ "ISO-8859-1",
50
+ "ISO-8859-1",
51
+ "Windows-1251",
52
+ "Windows-1251",
53
+ "Windows-1251",
54
+ "macRoman",
55
+ "UTF-16",
56
+ "UTF-16",
57
+ NULL,
58
+ "Windows-1256",
59
+ "Windows-1257",
60
+ "Windows-1257",
61
+ "UTF-32",
62
+ "UTF-32",
63
+ NULL,
64
+ "ASCII-8BIT",
65
+ NULL,
66
+ "US-ASCII",
67
+ "Windows-1250",
68
+ "Windows-1256",
69
+ "IBM866",
70
+ NULL,
71
+ "ISO-8859-7",
72
+ "ISO-8859-8",
73
+ NULL,
74
+ NULL,
75
+ "KOI8-R",
76
+ "KOI8-R",
77
+ NULL,
78
+ "ISO-8859-2",
79
+ "ISO-8859-9",
80
+ "ISO-8859-13",
81
+ "CP850",
82
+ "CP852",
83
+ NULL,
84
+ "UTF-8",
85
+ "Big5",
86
+ "EUC-KR",
87
+ "GB2312",
88
+ "GBK",
89
+ "Shift_JIS",
90
+ "TIS-620",
91
+ "UTF-16BE",
92
+ "eucJP-ms",
93
+ NULL,
94
+ NULL,
95
+ "ISO-8859-1",
96
+ "Windows-31J",
97
+ "Windows-31J",
98
+ "eucJP-ms",
99
+ "eucJP-ms",
100
+ "Windows-1250",
101
+ NULL,
102
+ "UTF-16",
103
+ "UTF-16",
104
+ "UTF-16",
105
+ "UTF-16",
106
+ "UTF-16",
107
+ "UTF-16",
108
+ "UTF-16",
109
+ "UTF-16",
110
+ "UTF-16",
111
+ "UTF-16",
112
+ "UTF-16",
113
+ "UTF-16",
114
+ "UTF-16",
115
+ "UTF-16",
116
+ "UTF-16",
117
+ "UTF-16",
118
+ "UTF-16",
119
+ "UTF-16",
120
+ "UTF-16",
121
+ "UTF-16",
122
+ NULL,
123
+ NULL,
124
+ NULL,
125
+ NULL,
126
+ NULL,
127
+ NULL,
128
+ NULL,
129
+ "UTF-16BE",
130
+ "UTF-16BE",
131
+ "UTF-16BE",
132
+ "UTF-16BE",
133
+ "UTF-16BE",
134
+ "UTF-16BE",
135
+ "UTF-16BE",
136
+ "UTF-16BE",
137
+ "UTF-16BE",
138
+ "UTF-16BE",
139
+ "UTF-16BE",
140
+ "UTF-16BE",
141
+ "UTF-16BE",
142
+ "UTF-16BE",
143
+ "UTF-16BE",
144
+ "UTF-16BE",
145
+ "UTF-16BE",
146
+ "UTF-16BE",
147
+ "UTF-16BE",
148
+ "UTF-16BE",
149
+ NULL,
150
+ NULL,
151
+ NULL,
152
+ NULL,
153
+ NULL,
154
+ NULL,
155
+ NULL,
156
+ NULL,
157
+ NULL,
158
+ NULL,
159
+ NULL,
160
+ NULL,
161
+ "UTF-32",
162
+ "UTF-32",
163
+ "UTF-32",
164
+ "UTF-32",
165
+ "UTF-32",
166
+ "UTF-32",
167
+ "UTF-32",
168
+ "UTF-32",
169
+ "UTF-32",
170
+ "UTF-32",
171
+ "UTF-32",
172
+ "UTF-32",
173
+ "UTF-32",
174
+ "UTF-32",
175
+ "UTF-32",
176
+ "UTF-32",
177
+ "UTF-32",
178
+ "UTF-32",
179
+ "UTF-32",
180
+ "UTF-32",
181
+ NULL,
182
+ NULL,
183
+ NULL,
184
+ NULL,
185
+ NULL,
186
+ NULL,
187
+ NULL,
188
+ NULL,
189
+ NULL,
190
+ NULL,
191
+ NULL,
192
+ NULL,
193
+ "UTF-8",
194
+ "UTF-8",
195
+ "UTF-8",
196
+ "UTF-8",
197
+ "UTF-8",
198
+ "UTF-8",
199
+ "UTF-8",
200
+ "UTF-8",
201
+ "UTF-8",
202
+ "UTF-8",
203
+ "UTF-8",
204
+ "UTF-8",
205
+ "UTF-8",
206
+ "UTF-8",
207
+ "UTF-8",
208
+ "UTF-8",
209
+ "UTF-8",
210
+ "UTF-8",
211
+ "UTF-8",
212
+ "UTF-8",
213
+ NULL,
214
+ NULL,
215
+ NULL,
216
+ NULL,
217
+ NULL,
218
+ NULL,
219
+ NULL,
220
+ NULL,
221
+ NULL,
222
+ NULL,
223
+ NULL,
224
+ NULL,
225
+ "UTF-8",
226
+ "UTF-8",
227
+ "UTF-8",
228
+ "UTF-8",
229
+ "UTF-8",
230
+ "UTF-8",
231
+ "UTF-8",
232
+ "UTF-8",
233
+ "UTF-8",
234
+ "UTF-8",
235
+ "UTF-8",
236
+ "UTF-8",
237
+ "UTF-8",
238
+ "UTF-8",
239
+ "UTF-8",
240
+ "UTF-8",
241
+ "UTF-8",
242
+ "UTF-8",
243
+ "UTF-8",
244
+ "UTF-8"
245
+ };
246
+
@@ -0,0 +1,684 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ #include <stdint.h>
4
+
5
+ #include "mysql_enc_to_ruby.h"
6
+
7
+ #ifdef HAVE_RUBY_ENCODING_H
8
+ static rb_encoding *binaryEncoding;
9
+ #endif
10
+
11
+ #if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
12
+ /* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
13
+ *
14
+ * (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
15
+ */
16
+ #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
+
27
+ #if defined(HAVE_RUBY_ENCODING_H)
28
+ /* 0000-1-1 00:00:00 UTC
29
+ *
30
+ * (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
31
+ */
32
+ #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
+
53
+ static VALUE cMysql2Result;
54
+ static VALUE cBigDecimal, cDate, cDateTime;
55
+ static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
56
+ extern VALUE mMysql2, cMysql2Client, cMysql2Error;
57
+ static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
58
+ static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
59
+ sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream, sym_name;
60
+ static ID intern_merge;
61
+
62
+ static void rb_mysql_result_mark(void * wrapper) {
63
+ mysql2_result_wrapper * w = wrapper;
64
+ if (w) {
65
+ rb_gc_mark(w->fields);
66
+ rb_gc_mark(w->rows);
67
+ rb_gc_mark(w->encoding);
68
+ rb_gc_mark(w->client);
69
+ }
70
+ }
71
+
72
+ /* this may be called manually or during GC */
73
+ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
74
+ if (wrapper && wrapper->resultFreed != 1) {
75
+ /* FIXME: this may call flush_use_result, which can hit the socket */
76
+ mysql_free_result(wrapper->result);
77
+ wrapper->resultFreed = 1;
78
+ }
79
+ }
80
+
81
+ /* this is called during GC */
82
+ static void rb_mysql_result_free(void *ptr) {
83
+ mysql2_result_wrapper * wrapper = ptr;
84
+ rb_mysql_result_free_result(wrapper);
85
+
86
+ // If the GC gets to client first it will be nil
87
+ if (wrapper->client != Qnil) {
88
+ decr_mysql2_client(wrapper->client_wrapper);
89
+ }
90
+
91
+ xfree(wrapper);
92
+ }
93
+
94
+ /*
95
+ * for small results, this won't hit the network, but there's no
96
+ * reliable way for us to tell this so we'll always release the GVL
97
+ * to be safe
98
+ */
99
+ static void *nogvl_fetch_row(void *ptr) {
100
+ MYSQL_RES *result = ptr;
101
+
102
+ return mysql_fetch_row(result);
103
+ }
104
+
105
+ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int symbolize_keys) {
106
+ mysql2_result_wrapper * wrapper;
107
+ VALUE rb_field;
108
+ GetMysql2Result(self, wrapper);
109
+
110
+ if (wrapper->fields == Qnil) {
111
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
112
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
113
+ }
114
+
115
+ rb_field = rb_ary_entry(wrapper->fields, idx);
116
+ if (rb_field == Qnil) {
117
+ MYSQL_FIELD *field = NULL;
118
+ #ifdef HAVE_RUBY_ENCODING_H
119
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
120
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
121
+ #endif
122
+
123
+ field = mysql_fetch_field_direct(wrapper->result, idx);
124
+ if (symbolize_keys) {
125
+ #ifdef HAVE_RB_INTERN3
126
+ rb_field = rb_intern3(field->name, field->name_length, rb_utf8_encoding());
127
+ rb_field = ID2SYM(rb_field);
128
+ #else
129
+ VALUE colStr;
130
+ colStr = rb_str_new(field->name, field->name_length);
131
+ rb_field = ID2SYM(rb_to_id(colStr));
132
+ #endif
133
+ } else {
134
+ rb_field = rb_str_new(field->name, field->name_length);
135
+ #ifdef HAVE_RUBY_ENCODING_H
136
+ rb_enc_associate(rb_field, conn_enc);
137
+ if (default_internal_enc) {
138
+ rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
139
+ }
140
+ #endif
141
+ }
142
+ rb_ary_store(wrapper->fields, idx, rb_field);
143
+ }
144
+
145
+ return rb_field;
146
+ }
147
+
148
+ #ifdef HAVE_RUBY_ENCODING_H
149
+ 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 it's wishes */
151
+ if (field.flags & BINARY_FLAG && field.charsetnr == 63) {
152
+ rb_enc_associate(val, binaryEncoding);
153
+ } else if (!field.charsetnr) {
154
+ /* MySQL 4.x may not provide an encoding, binary will get the bytes through */
155
+ rb_enc_associate(val, binaryEncoding);
156
+ } else {
157
+ /* lookup the encoding configured on this field */
158
+ const char *enc_name;
159
+ int enc_index;
160
+
161
+ enc_name = mysql2_mysql_enc_to_rb[field.charsetnr-1];
162
+ if (enc_name != NULL) {
163
+ /* use the field encoding we were able to match */
164
+ enc_index = rb_enc_find_index(enc_name);
165
+ rb_enc_set_index(val, enc_index);
166
+ } else {
167
+ /* otherwise fall-back to the connection's encoding */
168
+ rb_enc_associate(val, conn_enc);
169
+ }
170
+
171
+ if (default_internal_enc) {
172
+ val = rb_str_export_to_enc(val, default_internal_enc);
173
+ }
174
+ }
175
+ return val;
176
+ }
177
+ #endif
178
+
179
+ /* Interpret microseconds digits left-aligned in fixed-width field.
180
+ * e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
181
+ * because the microseconds are to the right of the decimal point.
182
+ */
183
+ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
184
+ {
185
+ int i;
186
+ for (i = 0; i < (len - 1); i++) {
187
+ if (msec_char[i] == '\0') {
188
+ msec_char[i] = '0';
189
+ }
190
+ }
191
+ return (unsigned int)strtoul(msec_char, NULL, 10);
192
+ }
193
+
194
+ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool, int cast, MYSQL_FIELD * fields) {
195
+ VALUE rowVal;
196
+ mysql2_result_wrapper * wrapper;
197
+ MYSQL_ROW row;
198
+ unsigned int i = 0;
199
+ unsigned long * fieldLengths;
200
+ void * ptr;
201
+ #ifdef HAVE_RUBY_ENCODING_H
202
+ rb_encoding *default_internal_enc;
203
+ rb_encoding *conn_enc;
204
+ #endif
205
+ GetMysql2Result(self, wrapper);
206
+
207
+ #ifdef HAVE_RUBY_ENCODING_H
208
+ default_internal_enc = rb_default_internal_encoding();
209
+ conn_enc = rb_to_encoding(wrapper->encoding);
210
+ #endif
211
+
212
+ ptr = wrapper->result;
213
+ row = (MYSQL_ROW)rb_thread_call_without_gvl(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
214
+ if (row == NULL) {
215
+ return Qnil;
216
+ }
217
+
218
+ if (asArray) {
219
+ rowVal = rb_ary_new2(wrapper->numberOfFields);
220
+ } else {
221
+ rowVal = rb_hash_new();
222
+ }
223
+ 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
+
229
+ for (i = 0; i < wrapper->numberOfFields; i++) {
230
+ VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
231
+ if (row[i]) {
232
+ VALUE val = Qnil;
233
+ enum enum_field_types type = fields[i].type;
234
+
235
+ if (!cast) {
236
+ if (type == MYSQL_TYPE_NULL) {
237
+ val = Qnil;
238
+ } else {
239
+ val = rb_str_new(row[i], fieldLengths[i]);
240
+ #ifdef HAVE_RUBY_ENCODING_H
241
+ val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
242
+ #endif
243
+ }
244
+ } else {
245
+ switch(type) {
246
+ case MYSQL_TYPE_NULL: /* NULL-type field */
247
+ val = Qnil;
248
+ break;
249
+ case MYSQL_TYPE_BIT: /* BIT field (MySQL 5.0.3 and up) */
250
+ if (castBool && fields[i].length == 1) {
251
+ val = *row[i] == 1 ? Qtrue : Qfalse;
252
+ }else{
253
+ val = rb_str_new(row[i], fieldLengths[i]);
254
+ }
255
+ break;
256
+ case MYSQL_TYPE_TINY: /* TINYINT field */
257
+ if (castBool && fields[i].length == 1) {
258
+ val = *row[i] != '0' ? Qtrue : Qfalse;
259
+ break;
260
+ }
261
+ case MYSQL_TYPE_SHORT: /* SMALLINT field */
262
+ case MYSQL_TYPE_LONG: /* INTEGER field */
263
+ case MYSQL_TYPE_INT24: /* MEDIUMINT field */
264
+ case MYSQL_TYPE_LONGLONG: /* BIGINT field */
265
+ case MYSQL_TYPE_YEAR: /* YEAR field */
266
+ val = rb_cstr2inum(row[i], 10);
267
+ break;
268
+ case MYSQL_TYPE_DECIMAL: /* DECIMAL or NUMERIC field */
269
+ case MYSQL_TYPE_NEWDECIMAL: /* Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) */
270
+ if (fields[i].decimals == 0) {
271
+ val = rb_cstr2inum(row[i], 10);
272
+ } else if (strtod(row[i], NULL) == 0.000000){
273
+ val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
274
+ }else{
275
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
276
+ }
277
+ break;
278
+ case MYSQL_TYPE_FLOAT: /* FLOAT field */
279
+ case MYSQL_TYPE_DOUBLE: { /* DOUBLE or REAL field */
280
+ double column_to_double;
281
+ column_to_double = strtod(row[i], NULL);
282
+ if (column_to_double == 0.000000){
283
+ val = opt_float_zero;
284
+ }else{
285
+ val = rb_float_new(column_to_double);
286
+ }
287
+ break;
288
+ }
289
+ case MYSQL_TYPE_TIME: { /* TIME field */
290
+ int tokens;
291
+ unsigned int hour=0, min=0, sec=0, msec=0;
292
+ char msec_char[7] = {'0','0','0','0','0','0','\0'};
293
+
294
+ tokens = sscanf(row[i], "%2u:%2u:%2u.%6s", &hour, &min, &sec, msec_char);
295
+ if (tokens < 3) {
296
+ val = Qnil;
297
+ break;
298
+ }
299
+ 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) {
303
+ val = rb_funcall(val, intern_localtime, 0);
304
+ } else { /* utc */
305
+ val = rb_funcall(val, intern_utc, 0);
306
+ }
307
+ }
308
+ break;
309
+ }
310
+ case MYSQL_TYPE_TIMESTAMP: /* TIMESTAMP field */
311
+ case MYSQL_TYPE_DATETIME: { /* DATETIME field */
312
+ int tokens;
313
+ unsigned int year=0, month=0, day=0, hour=0, min=0, sec=0, msec=0;
314
+ char msec_char[7] = {'0','0','0','0','0','0','\0'};
315
+ uint64_t seconds;
316
+
317
+ tokens = sscanf(row[i], "%4u-%2u-%2u %2u:%2u:%2u.%6s", &year, &month, &day, &hour, &min, &sec, msec_char);
318
+ if (tokens < 6) { /* msec might be empty */
319
+ val = Qnil;
320
+ break;
321
+ }
322
+ seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
323
+
324
+ if (seconds == 0) {
325
+ val = Qnil;
326
+ } else {
327
+ if (month < 1 || day < 1) {
328
+ rb_raise(cMysql2Error, "Invalid date in field '%.*s': %s", fields[i].name_length, fields[i].name, row[i]);
329
+ val = Qnil;
330
+ } else {
331
+ if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
332
+ VALUE offset = INT2NUM(0);
333
+ if (db_timezone == intern_local) {
334
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
335
+ }
336
+ 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) {
339
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
340
+ val = rb_funcall(val, intern_new_offset, 1, offset);
341
+ } else { /* utc */
342
+ val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
343
+ }
344
+ }
345
+ } else {
346
+ 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) {
350
+ val = rb_funcall(val, intern_localtime, 0);
351
+ } else { /* utc */
352
+ val = rb_funcall(val, intern_utc, 0);
353
+ }
354
+ }
355
+ }
356
+ }
357
+ }
358
+ break;
359
+ }
360
+ case MYSQL_TYPE_DATE: /* DATE field */
361
+ case MYSQL_TYPE_NEWDATE: { /* Newer const used > 5.0 */
362
+ int tokens;
363
+ unsigned int year=0, month=0, day=0;
364
+ tokens = sscanf(row[i], "%4u-%2u-%2u", &year, &month, &day);
365
+ if (tokens < 3) {
366
+ val = Qnil;
367
+ break;
368
+ }
369
+ if (year+month+day == 0) {
370
+ val = Qnil;
371
+ } else {
372
+ if (month < 1 || day < 1) {
373
+ rb_raise(cMysql2Error, "Invalid date in field '%.*s': %s", fields[i].name_length, fields[i].name, row[i]);
374
+ val = Qnil;
375
+ } else {
376
+ val = rb_funcall(cDate, intern_new, 3, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day));
377
+ }
378
+ }
379
+ break;
380
+ }
381
+ case MYSQL_TYPE_TINY_BLOB:
382
+ case MYSQL_TYPE_MEDIUM_BLOB:
383
+ case MYSQL_TYPE_LONG_BLOB:
384
+ case MYSQL_TYPE_BLOB:
385
+ case MYSQL_TYPE_VAR_STRING:
386
+ case MYSQL_TYPE_VARCHAR:
387
+ case MYSQL_TYPE_STRING: /* CHAR or BINARY field */
388
+ case MYSQL_TYPE_SET: /* SET field */
389
+ case MYSQL_TYPE_ENUM: /* ENUM field */
390
+ case MYSQL_TYPE_GEOMETRY: /* Spatial fielda */
391
+ default:
392
+ val = rb_str_new(row[i], fieldLengths[i]);
393
+ #ifdef HAVE_RUBY_ENCODING_H
394
+ val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc);
395
+ #endif
396
+ break;
397
+ }
398
+ }
399
+ if (asArray) {
400
+ rb_ary_push(rowVal, val);
401
+ } else {
402
+ rb_hash_aset(rowVal, field, val);
403
+ }
404
+ } else {
405
+ if (asArray) {
406
+ rb_ary_push(rowVal, Qnil);
407
+ } else {
408
+ rb_hash_aset(rowVal, field, Qnil);
409
+ }
410
+ }
411
+ }
412
+ return rowVal;
413
+ }
414
+
415
+ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
416
+ mysql2_result_wrapper * wrapper;
417
+ unsigned int i = 0;
418
+ short int symbolizeKeys = 0;
419
+ VALUE defaults;
420
+
421
+ GetMysql2Result(self, wrapper);
422
+
423
+ defaults = rb_iv_get(self, "@query_options");
424
+ Check_Type(defaults, T_HASH);
425
+ if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
426
+ symbolizeKeys = 1;
427
+ }
428
+
429
+ if (wrapper->fields == Qnil) {
430
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
431
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
432
+ }
433
+
434
+ if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
435
+ for (i=0; i<wrapper->numberOfFields; i++) {
436
+ rb_mysql_result_fetch_field(self, i, symbolizeKeys);
437
+ }
438
+ }
439
+
440
+ return wrapper->fields;
441
+ }
442
+
443
+ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
444
+ VALUE defaults, opts, block;
445
+ ID db_timezone, app_timezone, dbTz, appTz;
446
+ mysql2_result_wrapper * wrapper;
447
+ unsigned long i;
448
+ const char * errstr;
449
+ int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
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
+ }
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;
516
+ 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
+ }
525
+ }
526
+
527
+ if (streaming) {
528
+ if (!wrapper->streamingComplete) {
529
+ VALUE row;
530
+
531
+ fields = mysql_fetch_fields(wrapper->result);
532
+
533
+ do {
534
+ row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast, fields);
535
+
536
+ if (block != Qnil && row != Qnil) {
537
+ rb_yield(row);
538
+ wrapper->lastRowProcessed++;
539
+ }
540
+ } while(row != Qnil);
541
+
542
+ rb_mysql_result_free_result(wrapper);
543
+
544
+ wrapper->numberOfRows = wrapper->lastRowProcessed;
545
+ wrapper->streamingComplete = 1;
546
+
547
+ // Check for errors, the connection might have gone out from under us
548
+ // mysql_error returns an empty string if there is no error
549
+ errstr = mysql_error(wrapper->client_wrapper->client);
550
+ if (errstr[0]) {
551
+ rb_raise(cMysql2Error, "%s", errstr);
552
+ }
553
+ } else {
554
+ rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
555
+ }
556
+ } else {
557
+ if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
558
+ /* we've already read the entire dataset from the C result into our */
559
+ /* internal array. Lets hand that over to the user since it's ready to go */
560
+ for (i = 0; i < wrapper->numberOfRows; i++) {
561
+ rb_yield(rb_ary_entry(wrapper->rows, i));
562
+ }
563
+ } else {
564
+ unsigned long rowsProcessed = 0;
565
+ rowsProcessed = RARRAY_LEN(wrapper->rows);
566
+ fields = mysql_fetch_fields(wrapper->result);
567
+
568
+ for (i = 0; i < wrapper->numberOfRows; i++) {
569
+ VALUE row;
570
+ if (cacheRows && i < rowsProcessed) {
571
+ row = rb_ary_entry(wrapper->rows, i);
572
+ } else {
573
+ row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast, fields);
574
+ if (cacheRows) {
575
+ rb_ary_store(wrapper->rows, i, row);
576
+ }
577
+ wrapper->lastRowProcessed++;
578
+ }
579
+
580
+ if (row == Qnil) {
581
+ /* we don't need the mysql C dataset around anymore, peace it */
582
+ rb_mysql_result_free_result(wrapper);
583
+ return Qnil;
584
+ }
585
+
586
+ if (block != Qnil) {
587
+ rb_yield(row);
588
+ }
589
+ }
590
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
591
+ /* we don't need the mysql C dataset around anymore, peace it */
592
+ rb_mysql_result_free_result(wrapper);
593
+ }
594
+ }
595
+ }
596
+
597
+ return wrapper->rows;
598
+ }
599
+
600
+ static VALUE rb_mysql_result_count(VALUE self) {
601
+ mysql2_result_wrapper *wrapper;
602
+
603
+ GetMysql2Result(self, wrapper);
604
+ if (wrapper->resultFreed) {
605
+ if (wrapper->streamingComplete){
606
+ return LONG2NUM(wrapper->numberOfRows);
607
+ } else {
608
+ return LONG2NUM(RARRAY_LEN(wrapper->rows));
609
+ }
610
+ } else {
611
+ return INT2FIX(mysql_num_rows(wrapper->result));
612
+ }
613
+ }
614
+
615
+ /* Mysql2::Result */
616
+ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
617
+ VALUE obj;
618
+ mysql2_result_wrapper * wrapper;
619
+ obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
620
+ wrapper->numberOfFields = 0;
621
+ wrapper->numberOfRows = 0;
622
+ wrapper->lastRowProcessed = 0;
623
+ wrapper->resultFreed = 0;
624
+ wrapper->result = r;
625
+ wrapper->fields = Qnil;
626
+ wrapper->rows = Qnil;
627
+ wrapper->encoding = encoding;
628
+ wrapper->streamingComplete = 0;
629
+ wrapper->client = client;
630
+ wrapper->client_wrapper = DATA_PTR(client);
631
+ wrapper->client_wrapper->refcount++;
632
+
633
+ rb_obj_call_init(obj, 0, NULL);
634
+
635
+ rb_iv_set(obj, "@query_options", options);
636
+
637
+ return obj;
638
+ }
639
+
640
+ void init_mysql2_result() {
641
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
642
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
643
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
644
+
645
+ cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
646
+ rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
647
+ rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
648
+ rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
649
+ rb_define_alias(cMysql2Result, "size", "count");
650
+
651
+ intern_new = rb_intern("new");
652
+ intern_utc = rb_intern("utc");
653
+ intern_local = rb_intern("local");
654
+ intern_merge = rb_intern("merge");
655
+ intern_localtime = rb_intern("localtime");
656
+ intern_local_offset = rb_intern("local_offset");
657
+ intern_civil = rb_intern("civil");
658
+ intern_new_offset = rb_intern("new_offset");
659
+
660
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
661
+ sym_as = ID2SYM(rb_intern("as"));
662
+ sym_array = ID2SYM(rb_intern("array"));
663
+ sym_local = ID2SYM(rb_intern("local"));
664
+ sym_utc = ID2SYM(rb_intern("utc"));
665
+ sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
666
+ sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
667
+ sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
668
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
669
+ sym_cast = ID2SYM(rb_intern("cast"));
670
+ sym_stream = ID2SYM(rb_intern("stream"));
671
+ sym_name = ID2SYM(rb_intern("name"));
672
+
673
+ opt_decimal_zero = rb_str_new2("0.0");
674
+ rb_global_variable(&opt_decimal_zero); /*never GC */
675
+ opt_float_zero = rb_float_new((double)0);
676
+ rb_global_variable(&opt_float_zero);
677
+ opt_time_year = INT2NUM(2000);
678
+ opt_time_month = INT2NUM(1);
679
+ opt_utc_offset = INT2NUM(0);
680
+
681
+ #ifdef HAVE_RUBY_ENCODING_H
682
+ binaryEncoding = rb_enc_find("binary");
683
+ #endif
684
+ }