mysql2 0.2.6-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG.md +117 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.rdoc +240 -0
  6. data/Rakefile +5 -0
  7. data/VERSION +1 -0
  8. data/benchmark/active_record.rb +53 -0
  9. data/benchmark/allocations.rb +33 -0
  10. data/benchmark/escape.rb +39 -0
  11. data/benchmark/query_with_mysql_casting.rb +83 -0
  12. data/benchmark/query_without_mysql_casting.rb +50 -0
  13. data/benchmark/sequel.rb +39 -0
  14. data/benchmark/setup_db.rb +115 -0
  15. data/examples/eventmachine.rb +21 -0
  16. data/examples/threaded.rb +20 -0
  17. data/ext/mysql2/client.c +659 -0
  18. data/ext/mysql2/client.h +41 -0
  19. data/ext/mysql2/extconf.rb +65 -0
  20. data/ext/mysql2/mysql2_ext.c +12 -0
  21. data/ext/mysql2/mysql2_ext.h +32 -0
  22. data/ext/mysql2/result.c +475 -0
  23. data/ext/mysql2/result.h +20 -0
  24. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +63 -0
  25. data/lib/active_record/connection_adapters/mysql2_adapter.rb +654 -0
  26. data/lib/active_record/fiber_patches.rb +104 -0
  27. data/lib/arel/engines/sql/compilers/mysql2_compiler.rb +11 -0
  28. data/lib/mysql2.rb +16 -0
  29. data/lib/mysql2/client.rb +235 -0
  30. data/lib/mysql2/em.rb +33 -0
  31. data/lib/mysql2/error.rb +15 -0
  32. data/lib/mysql2/result.rb +5 -0
  33. data/mysql2.gemspec +89 -0
  34. data/spec/em/em_spec.rb +49 -0
  35. data/spec/mysql2/client_spec.rb +348 -0
  36. data/spec/mysql2/error_spec.rb +25 -0
  37. data/spec/mysql2/result_spec.rb +318 -0
  38. data/spec/rcov.opts +3 -0
  39. data/spec/spec_helper.rb +67 -0
  40. data/tasks/benchmarks.rake +8 -0
  41. data/tasks/compile.rake +54 -0
  42. data/tasks/jeweler.rake +17 -0
  43. data/tasks/rspec.rake +16 -0
  44. data/tasks/vendor_mysql.rake +41 -0
  45. metadata +120 -0
@@ -0,0 +1,41 @@
1
+ #ifndef MYSQL2_CLIENT_H
2
+ #define MYSQL2_CLIENT_H
3
+
4
+ /*
5
+ * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
6
+ * this is enough for dealing with blocking I/O functions in the
7
+ * presence of threads.
8
+ */
9
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
10
+
11
+ #include <rubysig.h>
12
+ #define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
13
+ typedef void rb_unblock_function_t(void *);
14
+ typedef VALUE rb_blocking_function_t(void *);
15
+ static VALUE
16
+ rb_thread_blocking_region(
17
+ rb_blocking_function_t *func, void *data1,
18
+ RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
19
+ RB_MYSQL_UNUSED void *data2)
20
+ {
21
+ VALUE rv;
22
+
23
+ TRAP_BEG;
24
+ rv = func(data1);
25
+ TRAP_END;
26
+
27
+ return rv;
28
+ }
29
+
30
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
31
+
32
+ void init_mysql2_client();
33
+
34
+ typedef struct {
35
+ VALUE encoding;
36
+ short int active;
37
+ short int closed;
38
+ MYSQL *client;
39
+ } mysql_client_wrapper;
40
+
41
+ #endif
@@ -0,0 +1,65 @@
1
+ # encoding: UTF-8
2
+ require 'mkmf'
3
+
4
+ def asplode lib
5
+ abort "-----\n#{lib} is missing. please check your installation of mysql and try again.\n-----"
6
+ end
7
+
8
+ # 1.9-only
9
+ have_func('rb_thread_blocking_region')
10
+
11
+ # borrowed from mysqlplus
12
+ # http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
13
+ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
14
+ /opt
15
+ /opt/local
16
+ /opt/local/mysql
17
+ /opt/local/lib/mysql5
18
+ /usr
19
+ /usr/local
20
+ /usr/local/mysql
21
+ /usr/local/mysql-*
22
+ /usr/local/lib/mysql5
23
+ ].map{|dir| "#{dir}/bin" }
24
+
25
+ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
26
+
27
+ if RUBY_PLATFORM =~ /mswin|mingw/
28
+ inc, lib = dir_config('mysql')
29
+ exit 1 unless have_library("libmysql")
30
+ elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
31
+ mc = Dir[GLOB].first if mc == true
32
+ cflags = `#{mc} --cflags`.chomp
33
+ exit 1 if $? != 0
34
+ libs = `#{mc} --libs`.chomp
35
+ exit 1 if $? != 0
36
+ $CPPFLAGS += ' ' + cflags
37
+ $libs = libs + " " + $libs
38
+ else
39
+ inc, lib = dir_config('mysql', '/usr/local')
40
+ libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
41
+ while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
42
+ exit 1 if libs.empty?
43
+ have_library(libs.shift)
44
+ end
45
+ end
46
+
47
+ if have_header('mysql.h') then
48
+ prefix = nil
49
+ elsif have_header('mysql/mysql.h') then
50
+ prefix = 'mysql'
51
+ else
52
+ asplode 'mysql.h'
53
+ end
54
+
55
+ %w{ errmsg.h mysqld_error.h }.each do |h|
56
+ header = [prefix, h].compact.join '/'
57
+ asplode h unless have_header h
58
+ end
59
+
60
+ unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/
61
+ $CFLAGS << ' -Wall -funroll-loops'
62
+ end
63
+ # $CFLAGS << ' -O0 -ggdb3 -Wextra'
64
+
65
+ create_makefile('mysql2/mysql2')
@@ -0,0 +1,12 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ VALUE mMysql2, cMysql2Error;
4
+
5
+ /* Ruby Extension initializer */
6
+ void Init_mysql2() {
7
+ mMysql2 = rb_define_module("Mysql2");
8
+ cMysql2Error = rb_const_get(mMysql2, rb_intern("Error"));
9
+
10
+ init_mysql2_client();
11
+ init_mysql2_result();
12
+ }
@@ -0,0 +1,32 @@
1
+ #ifndef MYSQL2_EXT
2
+ #define MYSQL2_EXT
3
+
4
+ #include <ruby.h>
5
+ #include <fcntl.h>
6
+
7
+ #ifdef HAVE_MYSQL_H
8
+ #include <mysql.h>
9
+ #include <mysql_com.h>
10
+ #include <errmsg.h>
11
+ #include <mysqld_error.h>
12
+ #else
13
+ #include <mysql/mysql.h>
14
+ #include <mysql/mysql_com.h>
15
+ #include <mysql/errmsg.h>
16
+ #include <mysql/mysqld_error.h>
17
+ #endif
18
+
19
+ #ifdef HAVE_RUBY_ENCODING_H
20
+ #include <ruby/encoding.h>
21
+ #endif
22
+
23
+ #if defined(__GNUC__) && (__GNUC__ >= 3)
24
+ #define RB_MYSQL_UNUSED __attribute__ ((unused))
25
+ #else
26
+ #define RB_MYSQL_UNUSED
27
+ #endif
28
+
29
+ #include <client.h>
30
+ #include <result.h>
31
+
32
+ #endif
@@ -0,0 +1,475 @@
1
+ #include <mysql2_ext.h>
2
+
3
+ #ifdef HAVE_RUBY_ENCODING_H
4
+ rb_encoding *binaryEncoding;
5
+ #endif
6
+
7
+ VALUE cMysql2Result;
8
+ VALUE cBigDecimal, cDate, cDateTime;
9
+ VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
10
+ extern VALUE mMysql2, cMysql2Client, cMysql2Error;
11
+ static VALUE intern_encoding_from_charset;
12
+ static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
13
+ intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
14
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
15
+ sym_local, sym_utc, sym_cast_booleans, sym_cache_rows;
16
+ static ID intern_merge;
17
+
18
+ static void rb_mysql_result_mark(void * wrapper) {
19
+ mysql2_result_wrapper * w = wrapper;
20
+ if (w) {
21
+ rb_gc_mark(w->fields);
22
+ rb_gc_mark(w->rows);
23
+ rb_gc_mark(w->encoding);
24
+ }
25
+ }
26
+
27
+ /* this may be called manually or during GC */
28
+ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
29
+ if (wrapper && wrapper->resultFreed != 1) {
30
+ mysql_free_result(wrapper->result);
31
+ wrapper->resultFreed = 1;
32
+ }
33
+ }
34
+
35
+ /* this is called during GC */
36
+ static void rb_mysql_result_free(void * wrapper) {
37
+ mysql2_result_wrapper * w = wrapper;
38
+ /* FIXME: this may call flush_use_result, which can hit the socket */
39
+ rb_mysql_result_free_result(w);
40
+ xfree(wrapper);
41
+ }
42
+
43
+ /*
44
+ * for small results, this won't hit the network, but there's no
45
+ * reliable way for us to tell this so we'll always release the GVL
46
+ * to be safe
47
+ */
48
+ static VALUE nogvl_fetch_row(void *ptr) {
49
+ MYSQL_RES *result = ptr;
50
+
51
+ return (VALUE)mysql_fetch_row(result);
52
+ }
53
+
54
+ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int symbolize_keys) {
55
+ mysql2_result_wrapper * wrapper;
56
+
57
+ GetMysql2Result(self, wrapper);
58
+
59
+ if (wrapper->fields == Qnil) {
60
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
61
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
62
+ }
63
+
64
+ VALUE rb_field = rb_ary_entry(wrapper->fields, idx);
65
+ if (rb_field == Qnil) {
66
+ MYSQL_FIELD *field = NULL;
67
+ #ifdef HAVE_RUBY_ENCODING_H
68
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
69
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
70
+ #endif
71
+
72
+ field = mysql_fetch_field_direct(wrapper->result, idx);
73
+ if (symbolize_keys) {
74
+ char buf[field->name_length+1];
75
+ memcpy(buf, field->name, field->name_length);
76
+ buf[field->name_length] = 0;
77
+ rb_field = ID2SYM(rb_intern(buf));
78
+ } else {
79
+ rb_field = rb_str_new(field->name, field->name_length);
80
+ #ifdef HAVE_RUBY_ENCODING_H
81
+ rb_enc_associate(rb_field, conn_enc);
82
+ if (default_internal_enc) {
83
+ rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
84
+ }
85
+ #endif
86
+ }
87
+ rb_ary_store(wrapper->fields, idx, rb_field);
88
+ }
89
+
90
+ return rb_field;
91
+ }
92
+
93
+ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool) {
94
+ VALUE rowVal;
95
+ mysql2_result_wrapper * wrapper;
96
+ MYSQL_ROW row;
97
+ MYSQL_FIELD * fields = NULL;
98
+ unsigned int i = 0;
99
+ unsigned long * fieldLengths;
100
+ void * ptr;
101
+
102
+ GetMysql2Result(self, wrapper);
103
+
104
+ #ifdef HAVE_RUBY_ENCODING_H
105
+ rb_encoding *default_internal_enc = rb_default_internal_encoding();
106
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
107
+ #endif
108
+
109
+ ptr = wrapper->result;
110
+ row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
111
+ if (row == NULL) {
112
+ return Qnil;
113
+ }
114
+
115
+ if (asArray) {
116
+ rowVal = rb_ary_new2(wrapper->numberOfFields);
117
+ } else {
118
+ rowVal = rb_hash_new();
119
+ }
120
+ fields = mysql_fetch_fields(wrapper->result);
121
+ fieldLengths = mysql_fetch_lengths(wrapper->result);
122
+ if (wrapper->fields == Qnil) {
123
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
124
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
125
+ }
126
+
127
+ for (i = 0; i < wrapper->numberOfFields; i++) {
128
+ VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
129
+ if (row[i]) {
130
+ VALUE val = Qnil;
131
+ switch(fields[i].type) {
132
+ case MYSQL_TYPE_NULL: // NULL-type field
133
+ val = Qnil;
134
+ break;
135
+ case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up)
136
+ val = rb_str_new(row[i], fieldLengths[i]);
137
+ break;
138
+ case MYSQL_TYPE_TINY: // TINYINT field
139
+ if (castBool && fields[i].length == 1) {
140
+ val = *row[i] == '1' ? Qtrue : Qfalse;
141
+ break;
142
+ }
143
+ case MYSQL_TYPE_SHORT: // SMALLINT field
144
+ case MYSQL_TYPE_LONG: // INTEGER field
145
+ case MYSQL_TYPE_INT24: // MEDIUMINT field
146
+ case MYSQL_TYPE_LONGLONG: // BIGINT field
147
+ case MYSQL_TYPE_YEAR: // YEAR field
148
+ val = rb_cstr2inum(row[i], 10);
149
+ break;
150
+ case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
151
+ case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
152
+ if (strtod(row[i], NULL) == 0.000000){
153
+ val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
154
+ }else{
155
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
156
+ }
157
+ break;
158
+ case MYSQL_TYPE_FLOAT: // FLOAT field
159
+ case MYSQL_TYPE_DOUBLE: { // DOUBLE or REAL field
160
+ double column_to_double;
161
+ column_to_double = strtod(row[i], NULL);
162
+ if (column_to_double == 0.000000){
163
+ val = opt_float_zero;
164
+ }else{
165
+ val = rb_float_new(column_to_double);
166
+ }
167
+ break;
168
+ }
169
+ case MYSQL_TYPE_TIME: { // TIME field
170
+ int hour, min, sec, tokens;
171
+ tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
172
+ val = rb_funcall(rb_cTime, db_timezone, 6, opt_time_year, opt_time_month, opt_time_month, INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
173
+ if (!NIL_P(app_timezone)) {
174
+ if (app_timezone == intern_local) {
175
+ val = rb_funcall(val, intern_localtime, 0);
176
+ } else { // utc
177
+ val = rb_funcall(val, intern_utc, 0);
178
+ }
179
+ }
180
+ break;
181
+ }
182
+ case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
183
+ case MYSQL_TYPE_DATETIME: { // DATETIME field
184
+ int year, month, day, hour, min, sec, tokens;
185
+ tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
186
+ if (year+month+day+hour+min+sec == 0) {
187
+ val = Qnil;
188
+ } else {
189
+ if (month < 1 || day < 1) {
190
+ rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
191
+ val = Qnil;
192
+ } else {
193
+ if (year < 1902 || year+month+day > 2058) { // use DateTime instead
194
+ VALUE offset = INT2NUM(0);
195
+ if (db_timezone == intern_local) {
196
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
197
+ }
198
+ val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), offset);
199
+ if (!NIL_P(app_timezone)) {
200
+ if (app_timezone == intern_local) {
201
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
202
+ val = rb_funcall(val, intern_new_offset, 1, offset);
203
+ } else { // utc
204
+ val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
205
+ }
206
+ }
207
+ } else {
208
+ val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
209
+ if (!NIL_P(app_timezone)) {
210
+ if (app_timezone == intern_local) {
211
+ val = rb_funcall(val, intern_localtime, 0);
212
+ } else { // utc
213
+ val = rb_funcall(val, intern_utc, 0);
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ break;
220
+ }
221
+ case MYSQL_TYPE_DATE: // DATE field
222
+ case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0
223
+ int year, month, day, tokens;
224
+ tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day);
225
+ if (year+month+day == 0) {
226
+ val = Qnil;
227
+ } else {
228
+ if (month < 1 || day < 1) {
229
+ rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
230
+ val = Qnil;
231
+ } else {
232
+ val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
233
+ }
234
+ }
235
+ break;
236
+ }
237
+ case MYSQL_TYPE_TINY_BLOB:
238
+ case MYSQL_TYPE_MEDIUM_BLOB:
239
+ case MYSQL_TYPE_LONG_BLOB:
240
+ case MYSQL_TYPE_BLOB:
241
+ case MYSQL_TYPE_VAR_STRING:
242
+ case MYSQL_TYPE_VARCHAR:
243
+ case MYSQL_TYPE_STRING: // CHAR or BINARY field
244
+ case MYSQL_TYPE_SET: // SET field
245
+ case MYSQL_TYPE_ENUM: // ENUM field
246
+ case MYSQL_TYPE_GEOMETRY: // Spatial fielda
247
+ default:
248
+ val = rb_str_new(row[i], fieldLengths[i]);
249
+ #ifdef HAVE_RUBY_ENCODING_H
250
+ // if binary flag is set, respect it's wishes
251
+ if (fields[i].flags & BINARY_FLAG) {
252
+ rb_enc_associate(val, binaryEncoding);
253
+ } else {
254
+ // lookup the encoding configured on this field
255
+ VALUE new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset_code, 1, INT2NUM(fields[i].charsetnr));
256
+ if (new_encoding != Qnil) {
257
+ // use the field encoding we were able to match
258
+ rb_encoding *enc = rb_to_encoding(new_encoding);
259
+ rb_enc_associate(val, enc);
260
+ } else {
261
+ // otherwise fall-back to the connection's encoding
262
+ rb_enc_associate(val, conn_enc);
263
+ }
264
+ if (default_internal_enc) {
265
+ val = rb_str_export_to_enc(val, default_internal_enc);
266
+ }
267
+ }
268
+ #endif
269
+ break;
270
+ }
271
+ if (asArray) {
272
+ rb_ary_push(rowVal, val);
273
+ } else {
274
+ rb_hash_aset(rowVal, field, val);
275
+ }
276
+ } else {
277
+ if (asArray) {
278
+ rb_ary_push(rowVal, Qnil);
279
+ } else {
280
+ rb_hash_aset(rowVal, field, Qnil);
281
+ }
282
+ }
283
+ }
284
+ return rowVal;
285
+ }
286
+
287
+ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
288
+ mysql2_result_wrapper * wrapper;
289
+ unsigned int i = 0;
290
+ short int symbolizeKeys = 0;
291
+ VALUE defaults;
292
+
293
+ GetMysql2Result(self, wrapper);
294
+
295
+ defaults = rb_iv_get(self, "@query_options");
296
+ if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
297
+ symbolizeKeys = 1;
298
+ }
299
+
300
+ if (wrapper->fields == Qnil) {
301
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
302
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
303
+ }
304
+
305
+ if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
306
+ for (i=0; i<wrapper->numberOfFields; i++) {
307
+ rb_mysql_result_fetch_field(self, i, symbolizeKeys);
308
+ }
309
+ }
310
+
311
+ return wrapper->fields;
312
+ }
313
+
314
+ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
315
+ VALUE defaults, opts, block;
316
+ ID db_timezone, app_timezone, dbTz, appTz;
317
+ mysql2_result_wrapper * wrapper;
318
+ unsigned long i;
319
+ int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1;
320
+
321
+ GetMysql2Result(self, wrapper);
322
+
323
+ defaults = rb_iv_get(self, "@query_options");
324
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
325
+ opts = rb_funcall(defaults, intern_merge, 1, opts);
326
+ } else {
327
+ opts = defaults;
328
+ }
329
+
330
+ if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
331
+ symbolizeKeys = 1;
332
+ }
333
+
334
+ if (rb_hash_aref(opts, sym_as) == sym_array) {
335
+ asArray = 1;
336
+ }
337
+
338
+ if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
339
+ castBool = 1;
340
+ }
341
+
342
+ if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
343
+ cacheRows = 0;
344
+ }
345
+
346
+ dbTz = rb_hash_aref(opts, sym_database_timezone);
347
+ if (dbTz == sym_local) {
348
+ db_timezone = intern_local;
349
+ } else if (dbTz == sym_utc) {
350
+ db_timezone = intern_utc;
351
+ } else {
352
+ if (!NIL_P(dbTz)) {
353
+ rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
354
+ }
355
+ db_timezone = intern_local;
356
+ }
357
+
358
+ appTz = rb_hash_aref(opts, sym_application_timezone);
359
+ if (appTz == sym_local) {
360
+ app_timezone = intern_local;
361
+ } else if (appTz == sym_utc) {
362
+ app_timezone = intern_utc;
363
+ } else {
364
+ app_timezone = Qnil;
365
+ }
366
+
367
+ if (wrapper->lastRowProcessed == 0) {
368
+ wrapper->numberOfRows = mysql_num_rows(wrapper->result);
369
+ if (wrapper->numberOfRows == 0) {
370
+ wrapper->rows = rb_ary_new();
371
+ return wrapper->rows;
372
+ }
373
+ wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
374
+ }
375
+
376
+ if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
377
+ // we've already read the entire dataset from the C result into our
378
+ // internal array. Lets hand that over to the user since it's ready to go
379
+ for (i = 0; i < wrapper->numberOfRows; i++) {
380
+ rb_yield(rb_ary_entry(wrapper->rows, i));
381
+ }
382
+ } else {
383
+ unsigned long rowsProcessed = 0;
384
+ rowsProcessed = RARRAY_LEN(wrapper->rows);
385
+ for (i = 0; i < wrapper->numberOfRows; i++) {
386
+ VALUE row;
387
+ if (cacheRows && i < rowsProcessed) {
388
+ row = rb_ary_entry(wrapper->rows, i);
389
+ } else {
390
+ row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
391
+ if (cacheRows) {
392
+ rb_ary_store(wrapper->rows, i, row);
393
+ }
394
+ wrapper->lastRowProcessed++;
395
+ }
396
+
397
+ if (row == Qnil) {
398
+ // we don't need the mysql C dataset around anymore, peace it
399
+ rb_mysql_result_free_result(wrapper);
400
+ return Qnil;
401
+ }
402
+
403
+ if (block != Qnil) {
404
+ rb_yield(row);
405
+ }
406
+ }
407
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
408
+ // we don't need the mysql C dataset around anymore, peace it
409
+ rb_mysql_result_free_result(wrapper);
410
+ }
411
+ }
412
+
413
+ return wrapper->rows;
414
+ }
415
+
416
+ /* Mysql2::Result */
417
+ VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
418
+ VALUE obj;
419
+ mysql2_result_wrapper * wrapper;
420
+ obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
421
+ wrapper->numberOfFields = 0;
422
+ wrapper->numberOfRows = 0;
423
+ wrapper->lastRowProcessed = 0;
424
+ wrapper->resultFreed = 0;
425
+ wrapper->result = r;
426
+ wrapper->fields = Qnil;
427
+ wrapper->rows = Qnil;
428
+ wrapper->encoding = Qnil;
429
+ rb_obj_call_init(obj, 0, NULL);
430
+ return obj;
431
+ }
432
+
433
+ void init_mysql2_result() {
434
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
435
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
436
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
437
+
438
+ cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
439
+ rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
440
+ rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
441
+
442
+ intern_encoding_from_charset = rb_intern("encoding_from_charset");
443
+ intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
444
+
445
+ intern_new = rb_intern("new");
446
+ intern_utc = rb_intern("utc");
447
+ intern_local = rb_intern("local");
448
+ intern_merge = rb_intern("merge");
449
+ intern_localtime = rb_intern("localtime");
450
+ intern_local_offset = rb_intern("local_offset");
451
+ intern_civil = rb_intern("civil");
452
+ intern_new_offset = rb_intern("new_offset");
453
+
454
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
455
+ sym_as = ID2SYM(rb_intern("as"));
456
+ sym_array = ID2SYM(rb_intern("array"));
457
+ sym_local = ID2SYM(rb_intern("local"));
458
+ sym_utc = ID2SYM(rb_intern("utc"));
459
+ sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
460
+ sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
461
+ sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
462
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
463
+
464
+ opt_decimal_zero = rb_str_new2("0.0");
465
+ rb_global_variable(&opt_decimal_zero); //never GC
466
+ opt_float_zero = rb_float_new((double)0);
467
+ rb_global_variable(&opt_float_zero);
468
+ opt_time_year = INT2NUM(2000);
469
+ opt_time_month = INT2NUM(1);
470
+ opt_utc_offset = INT2NUM(0);
471
+
472
+ #ifdef HAVE_RUBY_ENCODING_H
473
+ binaryEncoding = rb_enc_find("binary");
474
+ #endif
475
+ }