ghazel-mysql2 0.2.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) 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 +728 -0
  18. data/ext/mysql2/client.h +41 -0
  19. data/ext/mysql2/extconf.rb +69 -0
  20. data/ext/mysql2/mysql2_ext.c +12 -0
  21. data/ext/mysql2/mysql2_ext.h +38 -0
  22. data/ext/mysql2/result.c +478 -0
  23. data/ext/mysql2/result.h +20 -0
  24. data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +62 -0
  25. data/lib/active_record/connection_adapters/mysql2_adapter.rb +657 -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 +240 -0
  30. data/lib/mysql2/em.rb +37 -0
  31. data/lib/mysql2/em_fiber.rb +29 -0
  32. data/lib/mysql2/error.rb +15 -0
  33. data/lib/mysql2/result.rb +5 -0
  34. data/mysql2.gemspec +90 -0
  35. data/spec/em/em_spec.rb +49 -0
  36. data/spec/mysql2/client_spec.rb +367 -0
  37. data/spec/mysql2/error_spec.rb +25 -0
  38. data/spec/mysql2/result_spec.rb +318 -0
  39. data/spec/rcov.opts +3 -0
  40. data/spec/spec_helper.rb +67 -0
  41. data/tasks/benchmarks.rake +8 -0
  42. data/tasks/compile.rake +54 -0
  43. data/tasks/jeweler.rake +17 -0
  44. data/tasks/rspec.rake +16 -0
  45. data/tasks/vendor_mysql.rake +41 -0
  46. metadata +119 -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
+ char active;
37
+ char closed;
38
+ MYSQL *client;
39
+ } mysql_client_wrapper;
40
+
41
+ #endif
@@ -0,0 +1,69 @@
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
+ if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
66
+ $LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
67
+ end
68
+
69
+ 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,38 @@
1
+ #ifndef MYSQL2_EXT
2
+ #define MYSQL2_EXT
3
+
4
+ #include <ruby.h>
5
+ #include <fcntl.h>
6
+
7
+ #ifndef HAVE_UINT
8
+ #define HAVE_UINT
9
+ typedef unsigned short ushort;
10
+ typedef unsigned int uint;
11
+ #endif
12
+
13
+ #ifdef HAVE_MYSQL_H
14
+ #include <mysql.h>
15
+ #include <mysql_com.h>
16
+ #include <errmsg.h>
17
+ #include <mysqld_error.h>
18
+ #else
19
+ #include <mysql/mysql.h>
20
+ #include <mysql/mysql_com.h>
21
+ #include <mysql/errmsg.h>
22
+ #include <mysql/mysqld_error.h>
23
+ #endif
24
+
25
+ #ifdef HAVE_RUBY_ENCODING_H
26
+ #include <ruby/encoding.h>
27
+ #endif
28
+
29
+ #if defined(__GNUC__) && (__GNUC__ >= 3)
30
+ #define RB_MYSQL_UNUSED __attribute__ ((unused))
31
+ #else
32
+ #define RB_MYSQL_UNUSED
33
+ #endif
34
+
35
+ #include <client.h>
36
+ #include <result.h>
37
+
38
+ #endif
@@ -0,0 +1,478 @@
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
+ VALUE rb_field;
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
+ 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
+ #ifdef HAVE_RUBY_ENCODING_H
102
+ rb_encoding *default_internal_enc;
103
+ rb_encoding *conn_enc;
104
+ #endif
105
+ GetMysql2Result(self, wrapper);
106
+
107
+ #ifdef HAVE_RUBY_ENCODING_H
108
+ default_internal_enc = rb_default_internal_encoding();
109
+ conn_enc = rb_to_encoding(wrapper->encoding);
110
+ #endif
111
+
112
+ ptr = wrapper->result;
113
+ row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
114
+ if (row == NULL) {
115
+ return Qnil;
116
+ }
117
+
118
+ if (asArray) {
119
+ rowVal = rb_ary_new2(wrapper->numberOfFields);
120
+ } else {
121
+ rowVal = rb_hash_new();
122
+ }
123
+ fields = mysql_fetch_fields(wrapper->result);
124
+ fieldLengths = mysql_fetch_lengths(wrapper->result);
125
+ if (wrapper->fields == Qnil) {
126
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
127
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
128
+ }
129
+
130
+ for (i = 0; i < wrapper->numberOfFields; i++) {
131
+ VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
132
+ if (row[i]) {
133
+ VALUE val = Qnil;
134
+ switch(fields[i].type) {
135
+ case MYSQL_TYPE_NULL: // NULL-type field
136
+ val = Qnil;
137
+ break;
138
+ case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up)
139
+ val = rb_str_new(row[i], fieldLengths[i]);
140
+ break;
141
+ case MYSQL_TYPE_TINY: // TINYINT field
142
+ if (castBool && fields[i].length == 1) {
143
+ val = *row[i] == '1' ? Qtrue : Qfalse;
144
+ break;
145
+ }
146
+ case MYSQL_TYPE_SHORT: // SMALLINT field
147
+ case MYSQL_TYPE_LONG: // INTEGER field
148
+ case MYSQL_TYPE_INT24: // MEDIUMINT field
149
+ case MYSQL_TYPE_LONGLONG: // BIGINT field
150
+ case MYSQL_TYPE_YEAR: // YEAR field
151
+ val = rb_cstr2inum(row[i], 10);
152
+ break;
153
+ case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
154
+ case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
155
+ if (strtod(row[i], NULL) == 0.000000){
156
+ val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
157
+ }else{
158
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
159
+ }
160
+ break;
161
+ case MYSQL_TYPE_FLOAT: // FLOAT field
162
+ case MYSQL_TYPE_DOUBLE: { // DOUBLE or REAL field
163
+ double column_to_double;
164
+ column_to_double = strtod(row[i], NULL);
165
+ if (column_to_double == 0.000000){
166
+ val = opt_float_zero;
167
+ }else{
168
+ val = rb_float_new(column_to_double);
169
+ }
170
+ break;
171
+ }
172
+ case MYSQL_TYPE_TIME: { // TIME field
173
+ int hour, min, sec, tokens;
174
+ tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
175
+ val = rb_funcall(rb_cTime, db_timezone, 6, opt_time_year, opt_time_month, opt_time_month, INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
176
+ if (!NIL_P(app_timezone)) {
177
+ if (app_timezone == intern_local) {
178
+ val = rb_funcall(val, intern_localtime, 0);
179
+ } else { // utc
180
+ val = rb_funcall(val, intern_utc, 0);
181
+ }
182
+ }
183
+ break;
184
+ }
185
+ case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
186
+ case MYSQL_TYPE_DATETIME: { // DATETIME field
187
+ int year, month, day, hour, min, sec, tokens;
188
+ tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
189
+ if (year+month+day+hour+min+sec == 0) {
190
+ val = Qnil;
191
+ } else {
192
+ if (month < 1 || day < 1) {
193
+ rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
194
+ val = Qnil;
195
+ } else {
196
+ if (year < 1902 || year+month+day > 2058) { // use DateTime instead
197
+ VALUE offset = INT2NUM(0);
198
+ if (db_timezone == intern_local) {
199
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
200
+ }
201
+ val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), offset);
202
+ if (!NIL_P(app_timezone)) {
203
+ if (app_timezone == intern_local) {
204
+ offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
205
+ val = rb_funcall(val, intern_new_offset, 1, offset);
206
+ } else { // utc
207
+ val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
208
+ }
209
+ }
210
+ } else {
211
+ val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
212
+ if (!NIL_P(app_timezone)) {
213
+ if (app_timezone == intern_local) {
214
+ val = rb_funcall(val, intern_localtime, 0);
215
+ } else { // utc
216
+ val = rb_funcall(val, intern_utc, 0);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+ break;
223
+ }
224
+ case MYSQL_TYPE_DATE: // DATE field
225
+ case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0
226
+ int year, month, day, tokens;
227
+ tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day);
228
+ if (year+month+day == 0) {
229
+ val = Qnil;
230
+ } else {
231
+ if (month < 1 || day < 1) {
232
+ rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
233
+ val = Qnil;
234
+ } else {
235
+ val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
236
+ }
237
+ }
238
+ break;
239
+ }
240
+ case MYSQL_TYPE_TINY_BLOB:
241
+ case MYSQL_TYPE_MEDIUM_BLOB:
242
+ case MYSQL_TYPE_LONG_BLOB:
243
+ case MYSQL_TYPE_BLOB:
244
+ case MYSQL_TYPE_VAR_STRING:
245
+ case MYSQL_TYPE_VARCHAR:
246
+ case MYSQL_TYPE_STRING: // CHAR or BINARY field
247
+ case MYSQL_TYPE_SET: // SET field
248
+ case MYSQL_TYPE_ENUM: // ENUM field
249
+ case MYSQL_TYPE_GEOMETRY: // Spatial fielda
250
+ default:
251
+ val = rb_str_new(row[i], fieldLengths[i]);
252
+ #ifdef HAVE_RUBY_ENCODING_H
253
+ // if binary flag is set, respect it's wishes
254
+ if (fields[i].flags & BINARY_FLAG && fields[i].charsetnr == 63) {
255
+ rb_enc_associate(val, binaryEncoding);
256
+ } else {
257
+ // lookup the encoding configured on this field
258
+ VALUE new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset_code, 1, INT2NUM(fields[i].charsetnr));
259
+ if (new_encoding != Qnil) {
260
+ // use the field encoding we were able to match
261
+ rb_encoding *enc = rb_to_encoding(new_encoding);
262
+ rb_enc_associate(val, enc);
263
+ } else {
264
+ // otherwise fall-back to the connection's encoding
265
+ rb_enc_associate(val, conn_enc);
266
+ }
267
+ if (default_internal_enc) {
268
+ val = rb_str_export_to_enc(val, default_internal_enc);
269
+ }
270
+ }
271
+ #endif
272
+ break;
273
+ }
274
+ if (asArray) {
275
+ rb_ary_push(rowVal, val);
276
+ } else {
277
+ rb_hash_aset(rowVal, field, val);
278
+ }
279
+ } else {
280
+ if (asArray) {
281
+ rb_ary_push(rowVal, Qnil);
282
+ } else {
283
+ rb_hash_aset(rowVal, field, Qnil);
284
+ }
285
+ }
286
+ }
287
+ return rowVal;
288
+ }
289
+
290
+ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
291
+ mysql2_result_wrapper * wrapper;
292
+ unsigned int i = 0;
293
+ short int symbolizeKeys = 0;
294
+ VALUE defaults;
295
+
296
+ GetMysql2Result(self, wrapper);
297
+
298
+ defaults = rb_iv_get(self, "@query_options");
299
+ if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
300
+ symbolizeKeys = 1;
301
+ }
302
+
303
+ if (wrapper->fields == Qnil) {
304
+ wrapper->numberOfFields = mysql_num_fields(wrapper->result);
305
+ wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
306
+ }
307
+
308
+ if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
309
+ for (i=0; i<wrapper->numberOfFields; i++) {
310
+ rb_mysql_result_fetch_field(self, i, symbolizeKeys);
311
+ }
312
+ }
313
+
314
+ return wrapper->fields;
315
+ }
316
+
317
+ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
318
+ VALUE defaults, opts, block;
319
+ ID db_timezone, app_timezone, dbTz, appTz;
320
+ mysql2_result_wrapper * wrapper;
321
+ unsigned long i;
322
+ int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1;
323
+
324
+ GetMysql2Result(self, wrapper);
325
+
326
+ defaults = rb_iv_get(self, "@query_options");
327
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
328
+ opts = rb_funcall(defaults, intern_merge, 1, opts);
329
+ } else {
330
+ opts = defaults;
331
+ }
332
+
333
+ if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
334
+ symbolizeKeys = 1;
335
+ }
336
+
337
+ if (rb_hash_aref(opts, sym_as) == sym_array) {
338
+ asArray = 1;
339
+ }
340
+
341
+ if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
342
+ castBool = 1;
343
+ }
344
+
345
+ if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
346
+ cacheRows = 0;
347
+ }
348
+
349
+ dbTz = rb_hash_aref(opts, sym_database_timezone);
350
+ if (dbTz == sym_local) {
351
+ db_timezone = intern_local;
352
+ } else if (dbTz == sym_utc) {
353
+ db_timezone = intern_utc;
354
+ } else {
355
+ if (!NIL_P(dbTz)) {
356
+ rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
357
+ }
358
+ db_timezone = intern_local;
359
+ }
360
+
361
+ appTz = rb_hash_aref(opts, sym_application_timezone);
362
+ if (appTz == sym_local) {
363
+ app_timezone = intern_local;
364
+ } else if (appTz == sym_utc) {
365
+ app_timezone = intern_utc;
366
+ } else {
367
+ app_timezone = Qnil;
368
+ }
369
+
370
+ if (wrapper->lastRowProcessed == 0) {
371
+ wrapper->numberOfRows = mysql_num_rows(wrapper->result);
372
+ if (wrapper->numberOfRows == 0) {
373
+ wrapper->rows = rb_ary_new();
374
+ return wrapper->rows;
375
+ }
376
+ wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
377
+ }
378
+
379
+ if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
380
+ // we've already read the entire dataset from the C result into our
381
+ // internal array. Lets hand that over to the user since it's ready to go
382
+ for (i = 0; i < wrapper->numberOfRows; i++) {
383
+ rb_yield(rb_ary_entry(wrapper->rows, i));
384
+ }
385
+ } else {
386
+ unsigned long rowsProcessed = 0;
387
+ rowsProcessed = RARRAY_LEN(wrapper->rows);
388
+ for (i = 0; i < wrapper->numberOfRows; i++) {
389
+ VALUE row;
390
+ if (cacheRows && i < rowsProcessed) {
391
+ row = rb_ary_entry(wrapper->rows, i);
392
+ } else {
393
+ row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
394
+ if (cacheRows) {
395
+ rb_ary_store(wrapper->rows, i, row);
396
+ }
397
+ wrapper->lastRowProcessed++;
398
+ }
399
+
400
+ if (row == Qnil) {
401
+ // we don't need the mysql C dataset around anymore, peace it
402
+ rb_mysql_result_free_result(wrapper);
403
+ return Qnil;
404
+ }
405
+
406
+ if (block != Qnil) {
407
+ rb_yield(row);
408
+ }
409
+ }
410
+ if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
411
+ // we don't need the mysql C dataset around anymore, peace it
412
+ rb_mysql_result_free_result(wrapper);
413
+ }
414
+ }
415
+
416
+ return wrapper->rows;
417
+ }
418
+
419
+ /* Mysql2::Result */
420
+ VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
421
+ VALUE obj;
422
+ mysql2_result_wrapper * wrapper;
423
+ obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
424
+ wrapper->numberOfFields = 0;
425
+ wrapper->numberOfRows = 0;
426
+ wrapper->lastRowProcessed = 0;
427
+ wrapper->resultFreed = 0;
428
+ wrapper->result = r;
429
+ wrapper->fields = Qnil;
430
+ wrapper->rows = Qnil;
431
+ wrapper->encoding = Qnil;
432
+ rb_obj_call_init(obj, 0, NULL);
433
+ return obj;
434
+ }
435
+
436
+ void init_mysql2_result() {
437
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
438
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
439
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
440
+
441
+ cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
442
+ rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
443
+ rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
444
+
445
+ intern_encoding_from_charset = rb_intern("encoding_from_charset");
446
+ intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
447
+
448
+ intern_new = rb_intern("new");
449
+ intern_utc = rb_intern("utc");
450
+ intern_local = rb_intern("local");
451
+ intern_merge = rb_intern("merge");
452
+ intern_localtime = rb_intern("localtime");
453
+ intern_local_offset = rb_intern("local_offset");
454
+ intern_civil = rb_intern("civil");
455
+ intern_new_offset = rb_intern("new_offset");
456
+
457
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
458
+ sym_as = ID2SYM(rb_intern("as"));
459
+ sym_array = ID2SYM(rb_intern("array"));
460
+ sym_local = ID2SYM(rb_intern("local"));
461
+ sym_utc = ID2SYM(rb_intern("utc"));
462
+ sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
463
+ sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
464
+ sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
465
+ sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
466
+
467
+ opt_decimal_zero = rb_str_new2("0.0");
468
+ rb_global_variable(&opt_decimal_zero); //never GC
469
+ opt_float_zero = rb_float_new((double)0);
470
+ rb_global_variable(&opt_float_zero);
471
+ opt_time_year = INT2NUM(2000);
472
+ opt_time_month = INT2NUM(1);
473
+ opt_utc_offset = INT2NUM(0);
474
+
475
+ #ifdef HAVE_RUBY_ENCODING_H
476
+ binaryEncoding = rb_enc_find("binary");
477
+ #endif
478
+ }