mysql2 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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