mysql2 0.1.8 → 0.1.9
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.
- data/CHANGELOG.md +16 -0
- data/README.rdoc +12 -4
- data/Rakefile +10 -3
- data/VERSION +1 -1
- data/benchmark/active_record.rb +0 -1
- data/benchmark/escape.rb +2 -2
- data/benchmark/query_with_mysql_casting.rb +2 -2
- data/benchmark/query_without_mysql_casting.rb +2 -2
- data/benchmark/sequel.rb +0 -1
- data/benchmark/setup_db.rb +2 -2
- data/ext/{extconf.rb → mysql2/extconf.rb} +11 -31
- data/ext/mysql2/mysql2_ext.c +496 -0
- data/ext/{mysql2_ext.h → mysql2/mysql2_ext.h} +11 -39
- data/ext/mysql2/result.c +340 -0
- data/ext/mysql2/result.h +7 -0
- data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -7
- data/lib/active_record/fiber_patches.rb +104 -0
- data/lib/mysql2.rb +9 -3
- data/lib/mysql2/client.rb +211 -0
- data/lib/mysql2/error.rb +11 -0
- data/lib/mysql2/result.rb +5 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/mysql2.gemspec +15 -8
- data/spec/active_record/active_record_spec.rb +6 -4
- data/spec/em/em_spec.rb +2 -2
- data/spec/mysql2/client_spec.rb +48 -2
- data/spec/mysql2/error_spec.rb +2 -2
- data/spec/mysql2/result_spec.rb +127 -3
- metadata +20 -8
- data/ext/mysql2_ext.c +0 -748
@@ -1,3 +1,6 @@
|
|
1
|
+
#ifndef MYSQL2_EXT
|
2
|
+
#define MYSQL2_EXT
|
3
|
+
|
1
4
|
#include <ruby.h>
|
2
5
|
#include <fcntl.h>
|
3
6
|
|
@@ -15,7 +18,6 @@
|
|
15
18
|
|
16
19
|
#ifdef HAVE_RUBY_ENCODING_H
|
17
20
|
#include <ruby/encoding.h>
|
18
|
-
static int utf8Encoding, binaryEncoding;
|
19
21
|
#endif
|
20
22
|
|
21
23
|
#if defined(__GNUC__) && (__GNUC__ >= 3)
|
@@ -24,56 +26,24 @@ static int utf8Encoding, binaryEncoding;
|
|
24
26
|
#define RB_MYSQL_UNUSED
|
25
27
|
#endif
|
26
28
|
|
27
|
-
|
28
|
-
static ID intern_new, intern_utc;
|
29
|
+
#include <result.h>
|
29
30
|
|
30
|
-
|
31
|
-
static VALUE cMysql2Error;
|
31
|
+
extern VALUE mMysql2;
|
32
32
|
|
33
|
-
/* Mysql2::
|
34
|
-
|
35
|
-
MYSQL * client;
|
36
|
-
} mysql2_client_wrapper;
|
37
|
-
#define GetMysql2Client(obj, sval) (sval = ((mysql2_client_wrapper*)(DATA_PTR(obj)))->client);
|
38
|
-
static ID sym_socket, sym_host, sym_port, sym_username, sym_password,
|
39
|
-
sym_database, sym_reconnect, sym_connect_timeout, sym_id, sym_version,
|
40
|
-
sym_sslkey, sym_sslcert, sym_sslca, sym_sslcapath, sym_sslcipher,
|
41
|
-
sym_symbolize_keys, sym_async;
|
42
|
-
static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass);
|
43
|
-
static VALUE rb_mysql_client_init(int argc, VALUE * argv, VALUE self);
|
44
|
-
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self);
|
45
|
-
static VALUE rb_mysql_client_escape(VALUE self, VALUE str);
|
46
|
-
static VALUE rb_mysql_client_info(VALUE self);
|
47
|
-
static VALUE rb_mysql_client_server_info(VALUE self);
|
48
|
-
static VALUE rb_mysql_client_socket(VALUE self);
|
49
|
-
static VALUE rb_mysql_client_async_result(VALUE self);
|
50
|
-
static VALUE rb_mysql_client_last_id(VALUE self);
|
51
|
-
static VALUE rb_mysql_client_affected_rows(VALUE self);
|
52
|
-
static void rb_mysql_client_free(void * client);
|
33
|
+
/* Mysql2::Error */
|
34
|
+
extern VALUE cMysql2Error;
|
53
35
|
|
54
36
|
/* Mysql2::Result */
|
55
37
|
typedef struct {
|
56
38
|
VALUE fields;
|
57
39
|
VALUE rows;
|
58
|
-
unsigned
|
40
|
+
unsigned int numberOfFields;
|
59
41
|
unsigned long numberOfRows;
|
60
42
|
unsigned long lastRowProcessed;
|
61
|
-
int resultFreed;
|
43
|
+
short int resultFreed;
|
62
44
|
MYSQL_RES *result;
|
63
45
|
} mysql2_result_wrapper;
|
64
46
|
#define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
|
65
|
-
static VALUE cMysql2Result;
|
66
|
-
static VALUE rb_mysql_result_to_obj(MYSQL_RES * res);
|
67
|
-
static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self);
|
68
|
-
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self);
|
69
|
-
static void rb_mysql_result_free(void * wrapper);
|
70
|
-
static void rb_mysql_result_mark(void * wrapper);
|
71
|
-
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper);
|
72
|
-
|
73
|
-
/* Mysql2::Error */
|
74
|
-
static VALUE rb_raise_mysql2_error(MYSQL *client);
|
75
|
-
static VALUE rb_mysql_error_error_number(VALUE obj);
|
76
|
-
static VALUE rb_mysql_error_sql_state(VALUE obj);
|
77
47
|
|
78
48
|
/*
|
79
49
|
* used to pass all arguments to mysql_real_connect while inside
|
@@ -124,3 +94,5 @@ rb_thread_blocking_region(
|
|
124
94
|
return rv;
|
125
95
|
}
|
126
96
|
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
97
|
+
|
98
|
+
#endif
|
data/ext/mysql2/result.c
ADDED
@@ -0,0 +1,340 @@
|
|
1
|
+
#include <mysql2_ext.h>
|
2
|
+
|
3
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
4
|
+
rb_encoding *binaryEncoding;
|
5
|
+
#endif
|
6
|
+
|
7
|
+
ID sym_symbolize_keys;
|
8
|
+
ID intern_new, intern_utc, intern_encoding_from_charset_code;
|
9
|
+
|
10
|
+
VALUE cBigDecimal, cDate, cDateTime;
|
11
|
+
VALUE cMysql2Result;
|
12
|
+
extern VALUE cMysql2Client;
|
13
|
+
|
14
|
+
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
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
/* this may be called manually or during GC */
|
23
|
+
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
24
|
+
if (wrapper && wrapper->resultFreed != 1) {
|
25
|
+
mysql_free_result(wrapper->result);
|
26
|
+
wrapper->resultFreed = 1;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
/* this is called during GC */
|
31
|
+
static void rb_mysql_result_free(void * wrapper) {
|
32
|
+
mysql2_result_wrapper * w = wrapper;
|
33
|
+
/* FIXME: this may call flush_use_result, which can hit the socket */
|
34
|
+
rb_mysql_result_free_result(w);
|
35
|
+
xfree(wrapper);
|
36
|
+
}
|
37
|
+
|
38
|
+
/*
|
39
|
+
* for small results, this won't hit the network, but there's no
|
40
|
+
* reliable way for us to tell this so we'll always release the GVL
|
41
|
+
* to be safe
|
42
|
+
*/
|
43
|
+
static VALUE nogvl_fetch_row(void *ptr) {
|
44
|
+
MYSQL_RES *result = ptr;
|
45
|
+
|
46
|
+
return (VALUE)mysql_fetch_row(result);
|
47
|
+
}
|
48
|
+
|
49
|
+
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int symbolize_keys) {
|
50
|
+
mysql2_result_wrapper * wrapper;
|
51
|
+
|
52
|
+
GetMysql2Result(self, wrapper);
|
53
|
+
|
54
|
+
if (wrapper->fields == Qnil) {
|
55
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
56
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
57
|
+
}
|
58
|
+
|
59
|
+
VALUE rb_field = rb_ary_entry(wrapper->fields, idx);
|
60
|
+
if (rb_field == Qnil) {
|
61
|
+
MYSQL_FIELD *field = NULL;
|
62
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
63
|
+
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
64
|
+
rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding"));
|
65
|
+
#endif
|
66
|
+
|
67
|
+
field = mysql_fetch_field_direct(wrapper->result, idx);
|
68
|
+
if (symbolize_keys) {
|
69
|
+
char buf[field->name_length+1];
|
70
|
+
memcpy(buf, field->name, field->name_length);
|
71
|
+
buf[field->name_length] = 0;
|
72
|
+
rb_field = ID2SYM(rb_intern(buf));
|
73
|
+
} else {
|
74
|
+
rb_field = rb_str_new(field->name, field->name_length);
|
75
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
76
|
+
rb_enc_associate(rb_field, conn_enc);
|
77
|
+
if (default_internal_enc) {
|
78
|
+
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
79
|
+
}
|
80
|
+
#endif
|
81
|
+
}
|
82
|
+
rb_ary_store(wrapper->fields, idx, rb_field);
|
83
|
+
}
|
84
|
+
|
85
|
+
return rb_field;
|
86
|
+
}
|
87
|
+
|
88
|
+
static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
|
89
|
+
VALUE rowHash, opts, block;
|
90
|
+
mysql2_result_wrapper * wrapper;
|
91
|
+
MYSQL_ROW row;
|
92
|
+
MYSQL_FIELD * fields = NULL;
|
93
|
+
unsigned int i = 0, symbolizeKeys = 0;
|
94
|
+
unsigned long * fieldLengths;
|
95
|
+
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
|
+
GetMysql2Result(self, wrapper);
|
102
|
+
|
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
|
+
}
|
109
|
+
|
110
|
+
ptr = wrapper->result;
|
111
|
+
row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
|
112
|
+
if (row == NULL) {
|
113
|
+
return Qnil;
|
114
|
+
}
|
115
|
+
|
116
|
+
rowHash = rb_hash_new();
|
117
|
+
fields = mysql_fetch_fields(wrapper->result);
|
118
|
+
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
119
|
+
if (wrapper->fields == Qnil) {
|
120
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
121
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
122
|
+
}
|
123
|
+
|
124
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
125
|
+
VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys);
|
126
|
+
if (row[i]) {
|
127
|
+
VALUE val;
|
128
|
+
switch(fields[i].type) {
|
129
|
+
case MYSQL_TYPE_NULL: // NULL-type field
|
130
|
+
val = Qnil;
|
131
|
+
break;
|
132
|
+
case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up)
|
133
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
134
|
+
break;
|
135
|
+
case MYSQL_TYPE_TINY: // TINYINT field
|
136
|
+
case MYSQL_TYPE_SHORT: // SMALLINT field
|
137
|
+
case MYSQL_TYPE_LONG: // INTEGER field
|
138
|
+
case MYSQL_TYPE_INT24: // MEDIUMINT field
|
139
|
+
case MYSQL_TYPE_LONGLONG: // BIGINT field
|
140
|
+
case MYSQL_TYPE_YEAR: // YEAR field
|
141
|
+
val = rb_cstr2inum(row[i], 10);
|
142
|
+
break;
|
143
|
+
case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
|
144
|
+
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]));
|
146
|
+
break;
|
147
|
+
case MYSQL_TYPE_FLOAT: // FLOAT field
|
148
|
+
case MYSQL_TYPE_DOUBLE: // DOUBLE or REAL field
|
149
|
+
val = rb_float_new(strtod(row[i], NULL));
|
150
|
+
break;
|
151
|
+
case MYSQL_TYPE_TIME: { // TIME field
|
152
|
+
int hour, min, sec, tokens;
|
153
|
+
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));
|
155
|
+
break;
|
156
|
+
}
|
157
|
+
case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
|
158
|
+
case MYSQL_TYPE_DATETIME: { // DATETIME field
|
159
|
+
int year, month, day, hour, min, sec, tokens;
|
160
|
+
tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
|
161
|
+
if (year+month+day+hour+min+sec == 0) {
|
162
|
+
val = Qnil;
|
163
|
+
} else {
|
164
|
+
if (month < 1 || day < 1) {
|
165
|
+
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
166
|
+
val = Qnil;
|
167
|
+
} else {
|
168
|
+
val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
169
|
+
}
|
170
|
+
}
|
171
|
+
break;
|
172
|
+
}
|
173
|
+
case MYSQL_TYPE_DATE: // DATE field
|
174
|
+
case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0
|
175
|
+
int year, month, day, tokens;
|
176
|
+
tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day);
|
177
|
+
if (year+month+day == 0) {
|
178
|
+
val = Qnil;
|
179
|
+
} else {
|
180
|
+
if (month < 1 || day < 1) {
|
181
|
+
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
182
|
+
val = Qnil;
|
183
|
+
} else {
|
184
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
|
185
|
+
}
|
186
|
+
}
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
case MYSQL_TYPE_TINY_BLOB:
|
190
|
+
case MYSQL_TYPE_MEDIUM_BLOB:
|
191
|
+
case MYSQL_TYPE_LONG_BLOB:
|
192
|
+
case MYSQL_TYPE_BLOB:
|
193
|
+
case MYSQL_TYPE_VAR_STRING:
|
194
|
+
case MYSQL_TYPE_VARCHAR:
|
195
|
+
case MYSQL_TYPE_STRING: // CHAR or BINARY field
|
196
|
+
case MYSQL_TYPE_SET: // SET field
|
197
|
+
case MYSQL_TYPE_ENUM: // ENUM field
|
198
|
+
case MYSQL_TYPE_GEOMETRY: // Spatial fielda
|
199
|
+
default:
|
200
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
201
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
202
|
+
// if binary flag is set, respect it's wishes
|
203
|
+
if (fields[i].flags & BINARY_FLAG) {
|
204
|
+
rb_enc_associate(val, binaryEncoding);
|
205
|
+
} else {
|
206
|
+
// lookup the encoding configured on this field
|
207
|
+
VALUE new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset_code, 1, INT2NUM(fields[i].charsetnr));
|
208
|
+
if (new_encoding != Qnil) {
|
209
|
+
// use the field encoding we were able to match
|
210
|
+
rb_encoding *enc = rb_to_encoding(new_encoding);
|
211
|
+
rb_enc_associate(val, enc);
|
212
|
+
} else {
|
213
|
+
// otherwise fall-back to the connection's encoding
|
214
|
+
rb_enc_associate(val, conn_enc);
|
215
|
+
}
|
216
|
+
if (default_internal_enc) {
|
217
|
+
val = rb_str_export_to_enc(val, default_internal_enc);
|
218
|
+
}
|
219
|
+
}
|
220
|
+
#endif
|
221
|
+
break;
|
222
|
+
}
|
223
|
+
rb_hash_aset(rowHash, field, val);
|
224
|
+
} else {
|
225
|
+
rb_hash_aset(rowHash, field, Qnil);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return rowHash;
|
229
|
+
}
|
230
|
+
|
231
|
+
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
232
|
+
mysql2_result_wrapper * wrapper;
|
233
|
+
unsigned int i = 0;
|
234
|
+
|
235
|
+
GetMysql2Result(self, wrapper);
|
236
|
+
|
237
|
+
if (wrapper->fields == Qnil) {
|
238
|
+
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
239
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
240
|
+
}
|
241
|
+
|
242
|
+
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
243
|
+
for (i=0; i<wrapper->numberOfFields; i++) {
|
244
|
+
rb_mysql_result_fetch_field(self, i, 0);
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
return wrapper->fields;
|
249
|
+
}
|
250
|
+
|
251
|
+
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
252
|
+
VALUE opts, block;
|
253
|
+
mysql2_result_wrapper * wrapper;
|
254
|
+
unsigned long i;
|
255
|
+
|
256
|
+
GetMysql2Result(self, wrapper);
|
257
|
+
|
258
|
+
rb_scan_args(argc, argv, "01&", &opts, &block);
|
259
|
+
|
260
|
+
if (wrapper->lastRowProcessed == 0) {
|
261
|
+
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
262
|
+
if (wrapper->numberOfRows == 0) {
|
263
|
+
return Qnil;
|
264
|
+
}
|
265
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
266
|
+
}
|
267
|
+
|
268
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
269
|
+
// we've already read the entire dataset from the C result into our
|
270
|
+
// internal array. Lets hand that over to the user since it's ready to go
|
271
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
272
|
+
rb_yield(rb_ary_entry(wrapper->rows, i));
|
273
|
+
}
|
274
|
+
} else {
|
275
|
+
unsigned long rowsProcessed = 0;
|
276
|
+
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
277
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
278
|
+
VALUE row;
|
279
|
+
if (i < rowsProcessed) {
|
280
|
+
row = rb_ary_entry(wrapper->rows, i);
|
281
|
+
} else {
|
282
|
+
row = rb_mysql_result_fetch_row(argc, argv, self);
|
283
|
+
rb_ary_store(wrapper->rows, i, row);
|
284
|
+
wrapper->lastRowProcessed++;
|
285
|
+
}
|
286
|
+
|
287
|
+
if (row == Qnil) {
|
288
|
+
// we don't need the mysql C dataset around anymore, peace it
|
289
|
+
rb_mysql_result_free_result(wrapper);
|
290
|
+
return Qnil;
|
291
|
+
}
|
292
|
+
|
293
|
+
if (block != Qnil) {
|
294
|
+
rb_yield(row);
|
295
|
+
}
|
296
|
+
}
|
297
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
298
|
+
// we don't need the mysql C dataset around anymore, peace it
|
299
|
+
rb_mysql_result_free_result(wrapper);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
|
303
|
+
return wrapper->rows;
|
304
|
+
}
|
305
|
+
|
306
|
+
/* Mysql2::Result */
|
307
|
+
VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
|
308
|
+
VALUE obj;
|
309
|
+
mysql2_result_wrapper * wrapper;
|
310
|
+
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
311
|
+
wrapper->numberOfFields = 0;
|
312
|
+
wrapper->numberOfRows = 0;
|
313
|
+
wrapper->lastRowProcessed = 0;
|
314
|
+
wrapper->resultFreed = 0;
|
315
|
+
wrapper->result = r;
|
316
|
+
wrapper->fields = Qnil;
|
317
|
+
wrapper->rows = Qnil;
|
318
|
+
rb_obj_call_init(obj, 0, NULL);
|
319
|
+
return obj;
|
320
|
+
}
|
321
|
+
|
322
|
+
void init_mysql2_result()
|
323
|
+
{
|
324
|
+
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
325
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
326
|
+
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
327
|
+
|
328
|
+
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
329
|
+
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
330
|
+
rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
|
331
|
+
|
332
|
+
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
333
|
+
intern_new = rb_intern("new");
|
334
|
+
intern_utc = rb_intern("utc");
|
335
|
+
intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
|
336
|
+
|
337
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
338
|
+
binaryEncoding = rb_enc_find("binary");
|
339
|
+
#endif
|
340
|
+
}
|
data/ext/mysql2/result.h
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# AR adapter for using a fibered mysql2 connection with EM
|
4
|
+
# This adapter should be used within Thin or Unicorn with the rack-fiber_pool middleware.
|
5
|
+
# Just update your database.yml's adapter to be 'em_mysql2'
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
class Base
|
9
|
+
def self.em_mysql2_connection(config)
|
10
|
+
client = ::Mysql2::Fibered::Client.new(config.symbolize_keys)
|
11
|
+
options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
|
12
|
+
ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'fiber'
|
18
|
+
require 'eventmachine' unless defined? EventMachine
|
19
|
+
require 'mysql2' unless defined? Mysql2
|
20
|
+
require 'active_record/connection_adapters/mysql2_adapter'
|
21
|
+
require 'active_record/fiber_patches'
|
22
|
+
|
23
|
+
module Mysql2
|
24
|
+
module Fibered
|
25
|
+
class Client < ::Mysql2::Client
|
26
|
+
module Watcher
|
27
|
+
def initialize(client, deferable)
|
28
|
+
@client = client
|
29
|
+
@deferable = deferable
|
30
|
+
end
|
31
|
+
|
32
|
+
def notify_readable
|
33
|
+
begin
|
34
|
+
detach
|
35
|
+
results = @client.async_result
|
36
|
+
@deferable.succeed(results)
|
37
|
+
rescue Exception => e
|
38
|
+
puts e.backtrace.join("\n\t")
|
39
|
+
@deferable.fail(e)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
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)
|
54
|
+
end
|
55
|
+
Fiber.yield
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|