mysql2 0.1.9 → 0.2.0

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.
@@ -26,73 +26,7 @@
26
26
  #define RB_MYSQL_UNUSED
27
27
  #endif
28
28
 
29
+ #include <client.h>
29
30
  #include <result.h>
30
31
 
31
- extern VALUE mMysql2;
32
-
33
- /* Mysql2::Error */
34
- extern VALUE cMysql2Error;
35
-
36
- /* Mysql2::Result */
37
- typedef struct {
38
- VALUE fields;
39
- VALUE rows;
40
- unsigned int numberOfFields;
41
- unsigned long numberOfRows;
42
- unsigned long lastRowProcessed;
43
- short int resultFreed;
44
- MYSQL_RES *result;
45
- } mysql2_result_wrapper;
46
- #define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
47
-
48
- /*
49
- * used to pass all arguments to mysql_real_connect while inside
50
- * rb_thread_blocking_region
51
- */
52
- struct nogvl_connect_args {
53
- MYSQL *mysql;
54
- const char *host;
55
- const char *user;
56
- const char *passwd;
57
- const char *db;
58
- unsigned int port;
59
- const char *unix_socket;
60
- unsigned long client_flag;
61
- };
62
-
63
- /*
64
- * used to pass all arguments to mysql_send_query while inside
65
- * rb_thread_blocking_region
66
- */
67
- struct nogvl_send_query_args {
68
- MYSQL *mysql;
69
- VALUE sql;
70
- };
71
-
72
- /*
73
- * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
74
- * this is enough for dealing with blocking I/O functions in the
75
- * presence of threads.
76
- */
77
- #ifndef HAVE_RB_THREAD_BLOCKING_REGION
78
- # include <rubysig.h>
79
- # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
80
- typedef void rb_unblock_function_t(void *);
81
- typedef VALUE rb_blocking_function_t(void *);
82
- static VALUE
83
- rb_thread_blocking_region(
84
- rb_blocking_function_t *func, void *data1,
85
- RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
86
- RB_MYSQL_UNUSED void *data2)
87
- {
88
- VALUE rv;
89
-
90
- TRAP_BEG;
91
- rv = func(data1);
92
- TRAP_END;
93
-
94
- return rv;
95
- }
96
- #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
97
-
98
32
  #endif
@@ -4,19 +4,24 @@
4
4
  rb_encoding *binaryEncoding;
5
5
  #endif
6
6
 
7
- ID sym_symbolize_keys;
8
- ID intern_new, intern_utc, intern_encoding_from_charset_code;
9
-
10
- VALUE cBigDecimal, cDate, cDateTime;
11
7
  VALUE cMysql2Result;
12
- extern VALUE cMysql2Client;
8
+ VALUE cBigDecimal, cDate, cDateTime;
9
+ VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month;
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;
14
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
15
+ sym_local, sym_utc, sym_cast_booleans;
16
+ static ID intern_merge;
13
17
 
14
18
  static void rb_mysql_result_mark(void * wrapper) {
15
- mysql2_result_wrapper * w = wrapper;
16
- if (w) {
17
- rb_gc_mark(w->fields);
18
- rb_gc_mark(w->rows);
19
- }
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
+ }
20
25
  }
21
26
 
22
27
  /* this may be called manually or during GC */
@@ -61,7 +66,7 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
61
66
  MYSQL_FIELD *field = NULL;
62
67
  #ifdef HAVE_RUBY_ENCODING_H
63
68
  rb_encoding *default_internal_enc = rb_default_internal_encoding();
64
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
69
+ rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
65
70
  #endif
66
71
 
67
72
  field = mysql_fetch_field_direct(wrapper->result, idx);
@@ -85,27 +90,21 @@ static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int
85
90
  return rb_field;
86
91
  }
87
92
 
88
- static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
89
- VALUE rowHash, opts, block;
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;
90
95
  mysql2_result_wrapper * wrapper;
91
96
  MYSQL_ROW row;
92
97
  MYSQL_FIELD * fields = NULL;
93
- unsigned int i = 0, symbolizeKeys = 0;
98
+ unsigned int i = 0;
94
99
  unsigned long * fieldLengths;
95
100
  void * ptr;
96
- #ifdef HAVE_RUBY_ENCODING_H
97
- rb_encoding *default_internal_enc = rb_default_internal_encoding();
98
- rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
99
- #endif
100
101
 
101
102
  GetMysql2Result(self, wrapper);
102
103
 
103
- if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
104
- Check_Type(opts, T_HASH);
105
- if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
106
- symbolizeKeys = 1;
107
- }
108
- }
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
109
108
 
110
109
  ptr = wrapper->result;
111
110
  row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
@@ -113,7 +112,11 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
113
112
  return Qnil;
114
113
  }
115
114
 
116
- rowHash = rb_hash_new();
115
+ if (asArray) {
116
+ rowVal = rb_ary_new2(wrapper->numberOfFields);
117
+ } else {
118
+ rowVal = rb_hash_new();
119
+ }
117
120
  fields = mysql_fetch_fields(wrapper->result);
118
121
  fieldLengths = mysql_fetch_lengths(wrapper->result);
119
122
  if (wrapper->fields == Qnil) {
@@ -133,6 +136,10 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
133
136
  val = rb_str_new(row[i], fieldLengths[i]);
134
137
  break;
135
138
  case MYSQL_TYPE_TINY: // TINYINT field
139
+ if (castBool && fields[i].length == 1) {
140
+ val = *row[i] == '1' ? Qtrue : Qfalse;
141
+ break;
142
+ }
136
143
  case MYSQL_TYPE_SHORT: // SMALLINT field
137
144
  case MYSQL_TYPE_LONG: // INTEGER field
138
145
  case MYSQL_TYPE_INT24: // MEDIUMINT field
@@ -142,16 +149,34 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
142
149
  break;
143
150
  case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
144
151
  case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
145
- val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
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
+ }
146
157
  break;
147
158
  case MYSQL_TYPE_FLOAT: // FLOAT field
148
- case MYSQL_TYPE_DOUBLE: // DOUBLE or REAL field
149
- val = rb_float_new(strtod(row[i], NULL));
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
+ }
150
167
  break;
168
+ }
151
169
  case MYSQL_TYPE_TIME: { // TIME field
152
170
  int hour, min, sec, tokens;
153
171
  tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
154
- val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(0), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(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
+ }
155
180
  break;
156
181
  }
157
182
  case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
@@ -165,7 +190,14 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
165
190
  rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
166
191
  val = Qnil;
167
192
  } else {
168
- val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
193
+ val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
194
+ if (!NIL_P(app_timezone)) {
195
+ if (app_timezone == intern_local) {
196
+ val = rb_funcall(val, intern_localtime, 0);
197
+ } else { // utc
198
+ val = rb_funcall(val, intern_utc, 0);
199
+ }
200
+ }
169
201
  }
170
202
  }
171
203
  break;
@@ -220,20 +252,35 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
220
252
  #endif
221
253
  break;
222
254
  }
223
- rb_hash_aset(rowHash, field, val);
255
+ if (asArray) {
256
+ rb_ary_push(rowVal, val);
257
+ } else {
258
+ rb_hash_aset(rowVal, field, val);
259
+ }
224
260
  } else {
225
- rb_hash_aset(rowHash, field, Qnil);
261
+ if (asArray) {
262
+ rb_ary_push(rowVal, Qnil);
263
+ } else {
264
+ rb_hash_aset(rowVal, field, Qnil);
265
+ }
226
266
  }
227
267
  }
228
- return rowHash;
268
+ return rowVal;
229
269
  }
230
270
 
231
271
  static VALUE rb_mysql_result_fetch_fields(VALUE self) {
232
272
  mysql2_result_wrapper * wrapper;
233
273
  unsigned int i = 0;
274
+ short int symbolizeKeys = 0;
275
+ VALUE defaults;
234
276
 
235
277
  GetMysql2Result(self, wrapper);
236
278
 
279
+ defaults = rb_iv_get(self, "@query_options");
280
+ if (rb_hash_aref(defaults, sym_symbolize_keys) == Qtrue) {
281
+ symbolizeKeys = 1;
282
+ }
283
+
237
284
  if (wrapper->fields == Qnil) {
238
285
  wrapper->numberOfFields = mysql_num_fields(wrapper->result);
239
286
  wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
@@ -241,7 +288,7 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
241
288
 
242
289
  if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
243
290
  for (i=0; i<wrapper->numberOfFields; i++) {
244
- rb_mysql_result_fetch_field(self, i, 0);
291
+ rb_mysql_result_fetch_field(self, i, symbolizeKeys);
245
292
  }
246
293
  }
247
294
 
@@ -249,18 +296,59 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
249
296
  }
250
297
 
251
298
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
252
- VALUE opts, block;
299
+ VALUE defaults, opts, block;
300
+ ID db_timezone, app_timezone, dbTz, appTz;
253
301
  mysql2_result_wrapper * wrapper;
254
302
  unsigned long i;
303
+ int symbolizeKeys = 0, asArray = 0, castBool = 0;
255
304
 
256
305
  GetMysql2Result(self, wrapper);
257
306
 
258
- rb_scan_args(argc, argv, "01&", &opts, &block);
307
+ defaults = rb_iv_get(self, "@query_options");
308
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
309
+ opts = rb_funcall(defaults, intern_merge, 1, opts);
310
+ } else {
311
+ opts = defaults;
312
+ }
313
+
314
+ if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
315
+ symbolizeKeys = 1;
316
+ }
317
+
318
+ if (rb_hash_aref(opts, sym_as) == sym_array) {
319
+ asArray = 1;
320
+ }
321
+
322
+ if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
323
+ castBool = 1;
324
+ }
325
+
326
+ dbTz = rb_hash_aref(opts, sym_database_timezone);
327
+ if (dbTz == sym_local) {
328
+ db_timezone = intern_local;
329
+ } else if (dbTz == sym_utc) {
330
+ db_timezone = intern_utc;
331
+ } else {
332
+ if (!NIL_P(dbTz)) {
333
+ rb_warn(":database_timezone option must be :utc or :local - defaulting to :local");
334
+ }
335
+ db_timezone = intern_local;
336
+ }
337
+
338
+ appTz = rb_hash_aref(opts, sym_application_timezone);
339
+ if (appTz == sym_local) {
340
+ app_timezone = intern_local;
341
+ } else if (appTz == sym_utc) {
342
+ app_timezone = intern_utc;
343
+ } else {
344
+ app_timezone = Qnil;
345
+ }
259
346
 
260
347
  if (wrapper->lastRowProcessed == 0) {
261
348
  wrapper->numberOfRows = mysql_num_rows(wrapper->result);
262
349
  if (wrapper->numberOfRows == 0) {
263
- return Qnil;
350
+ wrapper->rows = rb_ary_new();
351
+ return wrapper->rows;
264
352
  }
265
353
  wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
266
354
  }
@@ -279,7 +367,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
279
367
  if (i < rowsProcessed) {
280
368
  row = rb_ary_entry(wrapper->rows, i);
281
369
  } else {
282
- row = rb_mysql_result_fetch_row(argc, argv, self);
370
+ row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool);
283
371
  rb_ary_store(wrapper->rows, i, row);
284
372
  wrapper->lastRowProcessed++;
285
373
  }
@@ -315,12 +403,12 @@ VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
315
403
  wrapper->result = r;
316
404
  wrapper->fields = Qnil;
317
405
  wrapper->rows = Qnil;
406
+ wrapper->encoding = Qnil;
318
407
  rb_obj_call_init(obj, 0, NULL);
319
408
  return obj;
320
409
  }
321
410
 
322
- void init_mysql2_result()
323
- {
411
+ void init_mysql2_result() {
324
412
  cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
325
413
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
326
414
  cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
@@ -329,11 +417,31 @@ void init_mysql2_result()
329
417
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
330
418
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
331
419
 
332
- sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
333
- intern_new = rb_intern("new");
334
- intern_utc = rb_intern("utc");
420
+ intern_encoding_from_charset = rb_intern("encoding_from_charset");
335
421
  intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
336
422
 
423
+ intern_new = rb_intern("new");
424
+ intern_utc = rb_intern("utc");
425
+ intern_local = rb_intern("local");
426
+ intern_merge = rb_intern("merge");
427
+ intern_localtime = rb_intern("localtime");
428
+
429
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
430
+ sym_as = ID2SYM(rb_intern("as"));
431
+ sym_array = ID2SYM(rb_intern("array"));
432
+ sym_local = ID2SYM(rb_intern("local"));
433
+ sym_utc = ID2SYM(rb_intern("utc"));
434
+ sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
435
+ sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
436
+ sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
437
+
438
+ rb_global_variable(&opt_decimal_zero); //never GC
439
+ opt_decimal_zero = rb_str_new2("0.0");
440
+ rb_global_variable(&opt_float_zero);
441
+ opt_float_zero = rb_float_new((double)0);
442
+ opt_time_year = INT2NUM(2000);
443
+ opt_time_month = INT2NUM(1);
444
+
337
445
  #ifdef HAVE_RUBY_ENCODING_H
338
446
  binaryEncoding = rb_enc_find("binary");
339
447
  #endif
@@ -4,4 +4,17 @@
4
4
  void init_mysql2_result();
5
5
  VALUE rb_mysql_result_to_obj(MYSQL_RES * r);
6
6
 
7
+ typedef struct {
8
+ VALUE fields;
9
+ VALUE rows;
10
+ VALUE encoding;
11
+ unsigned int numberOfFields;
12
+ unsigned long numberOfRows;
13
+ unsigned long lastRowProcessed;
14
+ short int resultFreed;
15
+ MYSQL_RES *result;
16
+ } mysql2_result_wrapper;
17
+
18
+ #define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
19
+
7
20
  #endif
@@ -42,17 +42,21 @@ module Mysql2
42
42
  end
43
43
 
44
44
  def query(sql, opts={})
45
- super(sql, opts.merge(:async => true))
46
- deferable = ::EM::DefaultDeferrable.new
47
- ::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
48
- fiber = Fiber.current
49
- deferable.callback do |result|
50
- fiber.resume(result)
51
- end
52
- deferable.errback do |err|
53
- fiber.resume(err)
45
+ if EM.reactor_running?
46
+ super(sql, opts.merge(:async => true))
47
+ deferable = ::EM::DefaultDeferrable.new
48
+ ::EM.watch(self.socket, Watcher, self, deferable).notify_readable = true
49
+ fiber = Fiber.current
50
+ deferable.callback do |result|
51
+ fiber.resume(result)
52
+ end
53
+ deferable.errback do |err|
54
+ fiber.resume(err)
55
+ end
56
+ Fiber.yield
57
+ else
58
+ super(sql, opts)
54
59
  end
55
- Fiber.yield
56
60
  end
57
61
  end
58
62
  end
@@ -11,5 +11,5 @@ require 'mysql2/result'
11
11
  #
12
12
  # A modern, simple and very fast Mysql library for Ruby - binding to libmysql
13
13
  module Mysql2
14
- VERSION = "0.1.9"
14
+ VERSION = "0.2.0"
15
15
  end