mysql2 0.3.11 → 0.3.12b1
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/.gitignore +2 -0
- data/.rbenv-version +1 -0
- data/Gemfile.lock +61 -0
- data/README.md +58 -0
- data/ext/mysql2/client.c +320 -61
- data/ext/mysql2/client.h +5 -3
- data/ext/mysql2/extconf.rb +6 -5
- data/ext/mysql2/result.c +91 -40
- data/ext/mysql2/result.h +1 -0
- data/lib/mysql2.rb +18 -2
- data/lib/mysql2/client.rb +39 -12
- data/lib/mysql2/em.rb +13 -3
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +11 -0
- data/spec/em/em_spec.rb +63 -3
- data/spec/mysql2/client_spec.rb +114 -12
- data/spec/mysql2/error_spec.rb +2 -2
- data/spec/mysql2/result_spec.rb +57 -5
- data/spec/spec_helper.rb +3 -1
- data/tasks/rspec.rake +11 -1
- metadata +149 -149
data/ext/mysql2/client.h
CHANGED
@@ -33,10 +33,12 @@ void init_mysql2_client();
|
|
33
33
|
|
34
34
|
typedef struct {
|
35
35
|
VALUE encoding;
|
36
|
-
|
36
|
+
VALUE active_thread; /* rb_thread_current() or Qnil */
|
37
37
|
int reconnect_enabled;
|
38
|
-
int
|
38
|
+
int active;
|
39
|
+
int connected;
|
40
|
+
int initialized;
|
39
41
|
MYSQL *client;
|
40
42
|
} mysql_client_wrapper;
|
41
43
|
|
42
|
-
#endif
|
44
|
+
#endif
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -17,6 +17,7 @@ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
|
|
17
17
|
/opt/local/mysql
|
18
18
|
/opt/local/lib/mysql5
|
19
19
|
/usr
|
20
|
+
/usr/mysql
|
20
21
|
/usr/local
|
21
22
|
/usr/local/mysql
|
22
23
|
/usr/local/mysql-*
|
@@ -61,13 +62,13 @@ end
|
|
61
62
|
asplode h unless have_header h
|
62
63
|
end
|
63
64
|
|
64
|
-
|
65
|
+
# GCC specific flags
|
66
|
+
if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
|
65
67
|
$CFLAGS << ' -Wall -funroll-loops'
|
66
|
-
end
|
67
|
-
# $CFLAGS << ' -O0 -ggdb3 -Wextra'
|
68
68
|
|
69
|
-
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
|
70
|
-
|
69
|
+
if hard_mysql_path = $libs[%r{-L(/[^ ]+)}, 1]
|
70
|
+
$LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
|
71
|
+
end
|
71
72
|
end
|
72
73
|
|
73
74
|
create_makefile('mysql2/mysql2')
|
data/ext/mysql2/result.c
CHANGED
@@ -55,7 +55,7 @@ static VALUE intern_encoding_from_charset;
|
|
55
55
|
static ID intern_new, intern_utc, intern_local, intern_encoding_from_charset_code,
|
56
56
|
intern_localtime, intern_local_offset, intern_civil, intern_new_offset;
|
57
57
|
static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone, sym_application_timezone,
|
58
|
-
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast;
|
58
|
+
sym_local, sym_utc, sym_cast_booleans, sym_cache_rows, sym_cast, sym_stream;
|
59
59
|
static ID intern_merge;
|
60
60
|
|
61
61
|
static void rb_mysql_result_mark(void * wrapper) {
|
@@ -163,11 +163,10 @@ static VALUE mysql2_set_field_string_encoding(VALUE val, MYSQL_FIELD field, rb_e
|
|
163
163
|
#endif
|
164
164
|
|
165
165
|
|
166
|
-
static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool, int cast) {
|
166
|
+
static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool, int cast, MYSQL_FIELD * fields) {
|
167
167
|
VALUE rowVal;
|
168
168
|
mysql2_result_wrapper * wrapper;
|
169
169
|
MYSQL_ROW row;
|
170
|
-
MYSQL_FIELD * fields = NULL;
|
171
170
|
unsigned int i = 0;
|
172
171
|
unsigned long * fieldLengths;
|
173
172
|
void * ptr;
|
@@ -193,7 +192,6 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
193
192
|
} else {
|
194
193
|
rowVal = rb_hash_new();
|
195
194
|
}
|
196
|
-
fields = mysql_fetch_fields(wrapper->result);
|
197
195
|
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
198
196
|
if (wrapper->fields == Qnil) {
|
199
197
|
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
@@ -225,7 +223,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
225
223
|
break;
|
226
224
|
case MYSQL_TYPE_TINY: // TINYINT field
|
227
225
|
if (castBool && fields[i].length == 1) {
|
228
|
-
val = *row[i]
|
226
|
+
val = *row[i] != '0' ? Qtrue : Qfalse;
|
229
227
|
break;
|
230
228
|
}
|
231
229
|
case MYSQL_TYPE_SHORT: // SMALLINT field
|
@@ -237,7 +235,9 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
237
235
|
break;
|
238
236
|
case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
|
239
237
|
case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
|
240
|
-
if (
|
238
|
+
if (fields[i].decimals == 0) {
|
239
|
+
val = rb_cstr2inum(row[i], 10);
|
240
|
+
} else if (strtod(row[i], NULL) == 0.000000){
|
241
241
|
val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero);
|
242
242
|
}else{
|
243
243
|
val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
|
@@ -392,7 +392,8 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
392
392
|
ID db_timezone, app_timezone, dbTz, appTz;
|
393
393
|
mysql2_result_wrapper * wrapper;
|
394
394
|
unsigned long i;
|
395
|
-
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1;
|
395
|
+
int symbolizeKeys = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
|
396
|
+
MYSQL_FIELD * fields = NULL;
|
396
397
|
|
397
398
|
GetMysql2Result(self, wrapper);
|
398
399
|
|
@@ -423,6 +424,14 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
423
424
|
cast = 0;
|
424
425
|
}
|
425
426
|
|
427
|
+
if(rb_hash_aref(opts, sym_stream) == Qtrue) {
|
428
|
+
streaming = 1;
|
429
|
+
}
|
430
|
+
|
431
|
+
if(streaming && cacheRows) {
|
432
|
+
rb_warn("cacheRows is ignored if streaming is true");
|
433
|
+
}
|
434
|
+
|
426
435
|
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
427
436
|
if (dbTz == sym_local) {
|
428
437
|
db_timezone = intern_local;
|
@@ -445,49 +454,82 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
445
454
|
}
|
446
455
|
|
447
456
|
if (wrapper->lastRowProcessed == 0) {
|
448
|
-
|
449
|
-
|
457
|
+
if(streaming) {
|
458
|
+
// We can't get number of rows if we're streaming,
|
459
|
+
// until we've finished fetching all rows
|
460
|
+
wrapper->numberOfRows = 0;
|
450
461
|
wrapper->rows = rb_ary_new();
|
451
|
-
|
462
|
+
} else {
|
463
|
+
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
464
|
+
if (wrapper->numberOfRows == 0) {
|
465
|
+
wrapper->rows = rb_ary_new();
|
466
|
+
return wrapper->rows;
|
467
|
+
}
|
468
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
452
469
|
}
|
453
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
454
470
|
}
|
455
471
|
|
456
|
-
if (
|
457
|
-
|
458
|
-
// internal array. Lets hand that over to the user since it's ready to go
|
459
|
-
for (i = 0; i < wrapper->numberOfRows; i++) {
|
460
|
-
rb_yield(rb_ary_entry(wrapper->rows, i));
|
461
|
-
}
|
462
|
-
} else {
|
463
|
-
unsigned long rowsProcessed = 0;
|
464
|
-
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
465
|
-
for (i = 0; i < wrapper->numberOfRows; i++) {
|
472
|
+
if (streaming) {
|
473
|
+
if(!wrapper->streamingComplete) {
|
466
474
|
VALUE row;
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
475
|
+
|
476
|
+
fields = mysql_fetch_fields(wrapper->result);
|
477
|
+
|
478
|
+
do {
|
479
|
+
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast, fields);
|
480
|
+
|
481
|
+
if (block != Qnil && row != Qnil) {
|
482
|
+
rb_yield(row);
|
483
|
+
wrapper->lastRowProcessed++;
|
473
484
|
}
|
474
|
-
|
485
|
+
} while(row != Qnil);
|
486
|
+
|
487
|
+
rb_mysql_result_free_result(wrapper);
|
488
|
+
|
489
|
+
wrapper->numberOfRows = wrapper->lastRowProcessed;
|
490
|
+
wrapper->streamingComplete = 1;
|
491
|
+
} else {
|
492
|
+
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
493
|
+
}
|
494
|
+
} else {
|
495
|
+
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
496
|
+
// we've already read the entire dataset from the C result into our
|
497
|
+
// internal array. Lets hand that over to the user since it's ready to go
|
498
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
499
|
+
rb_yield(rb_ary_entry(wrapper->rows, i));
|
475
500
|
}
|
501
|
+
} else {
|
502
|
+
unsigned long rowsProcessed = 0;
|
503
|
+
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
504
|
+
fields = mysql_fetch_fields(wrapper->result);
|
505
|
+
|
506
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
507
|
+
VALUE row;
|
508
|
+
if (cacheRows && i < rowsProcessed) {
|
509
|
+
row = rb_ary_entry(wrapper->rows, i);
|
510
|
+
} else {
|
511
|
+
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast, fields);
|
512
|
+
if (cacheRows) {
|
513
|
+
rb_ary_store(wrapper->rows, i, row);
|
514
|
+
}
|
515
|
+
wrapper->lastRowProcessed++;
|
516
|
+
}
|
476
517
|
|
477
|
-
|
518
|
+
if (row == Qnil) {
|
519
|
+
// we don't need the mysql C dataset around anymore, peace it
|
520
|
+
rb_mysql_result_free_result(wrapper);
|
521
|
+
return Qnil;
|
522
|
+
}
|
523
|
+
|
524
|
+
if (block != Qnil) {
|
525
|
+
rb_yield(row);
|
526
|
+
}
|
527
|
+
}
|
528
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
478
529
|
// we don't need the mysql C dataset around anymore, peace it
|
479
530
|
rb_mysql_result_free_result(wrapper);
|
480
|
-
return Qnil;
|
481
|
-
}
|
482
|
-
|
483
|
-
if (block != Qnil) {
|
484
|
-
rb_yield(row);
|
485
531
|
}
|
486
532
|
}
|
487
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
488
|
-
// we don't need the mysql C dataset around anymore, peace it
|
489
|
-
rb_mysql_result_free_result(wrapper);
|
490
|
-
}
|
491
533
|
}
|
492
534
|
|
493
535
|
return wrapper->rows;
|
@@ -497,8 +539,15 @@ static VALUE rb_mysql_result_count(VALUE self) {
|
|
497
539
|
mysql2_result_wrapper *wrapper;
|
498
540
|
|
499
541
|
GetMysql2Result(self, wrapper);
|
500
|
-
|
501
|
-
|
542
|
+
if(wrapper->resultFreed) {
|
543
|
+
if (wrapper->streamingComplete){
|
544
|
+
return LONG2NUM(wrapper->numberOfRows);
|
545
|
+
} else {
|
546
|
+
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
547
|
+
}
|
548
|
+
} else {
|
549
|
+
return INT2FIX(mysql_num_rows(wrapper->result));
|
550
|
+
}
|
502
551
|
}
|
503
552
|
|
504
553
|
/* Mysql2::Result */
|
@@ -514,6 +563,7 @@ VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
|
|
514
563
|
wrapper->fields = Qnil;
|
515
564
|
wrapper->rows = Qnil;
|
516
565
|
wrapper->encoding = Qnil;
|
566
|
+
wrapper->streamingComplete = 0;
|
517
567
|
rb_obj_call_init(obj, 0, NULL);
|
518
568
|
return obj;
|
519
569
|
}
|
@@ -551,6 +601,7 @@ void init_mysql2_result() {
|
|
551
601
|
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
|
552
602
|
sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
|
553
603
|
sym_cast = ID2SYM(rb_intern("cast"));
|
604
|
+
sym_stream = ID2SYM(rb_intern("stream"));
|
554
605
|
|
555
606
|
opt_decimal_zero = rb_str_new2("0.0");
|
556
607
|
rb_global_variable(&opt_decimal_zero); //never GC
|
data/ext/mysql2/result.h
CHANGED
data/lib/mysql2.rb
CHANGED
@@ -16,6 +16,22 @@ module Mysql2
|
|
16
16
|
end
|
17
17
|
|
18
18
|
if defined?(ActiveRecord::VERSION::STRING) && ActiveRecord::VERSION::STRING < "3.1"
|
19
|
-
|
20
|
-
|
19
|
+
warn "============= WARNING FROM mysql2 ============="
|
20
|
+
warn "This version of mysql2 (#{Mysql2::VERSION}) doesn't ship with the ActiveRecord adapter."
|
21
|
+
warn "In Rails version 3.1.0 and up, the mysql2 ActiveRecord adapter is included with rails."
|
22
|
+
warn "If you want to use the mysql2 gem with Rails <= 3.0.x, please use the latest mysql2 in the 0.2.x series."
|
23
|
+
warn "============= END WARNING FROM mysql2 ============="
|
24
|
+
end
|
25
|
+
|
26
|
+
# For holding utility methods
|
27
|
+
module Mysql2::Util
|
28
|
+
|
29
|
+
#
|
30
|
+
# Rekey a string-keyed hash with equivalent symbols.
|
31
|
+
#
|
32
|
+
def self.key_hash_as_symbols(hash)
|
33
|
+
return nil unless hash
|
34
|
+
Hash[hash.map { |k,v| [k.to_sym, v] }]
|
35
|
+
end
|
36
|
+
|
21
37
|
end
|
data/lib/mysql2/client.rb
CHANGED
@@ -14,31 +14,36 @@ module Mysql2
|
|
14
14
|
}
|
15
15
|
|
16
16
|
def initialize(opts = {})
|
17
|
+
opts = Mysql2::Util.key_hash_as_symbols( opts )
|
17
18
|
@query_options = @@default_query_options.dup
|
18
19
|
@query_options.merge! opts
|
19
20
|
|
20
|
-
|
21
|
+
initialize_ext
|
21
22
|
|
22
|
-
|
23
|
+
# Set MySQL connection options (each one is a call to mysql_options())
|
24
|
+
[:reconnect, :connect_timeout, :local_infile, :read_timeout].each do |key|
|
23
25
|
next unless opts.key?(key)
|
24
26
|
send(:"#{key}=", opts[key])
|
25
27
|
end
|
28
|
+
|
26
29
|
# force the encoding to utf8
|
27
30
|
self.charset_name = opts[:encoding] || 'utf8'
|
28
31
|
|
29
|
-
@read_timeout = opts[:read_timeout]
|
30
|
-
if @read_timeout and @read_timeout < 0
|
31
|
-
raise Mysql2::Error, "read_timeout must be a positive integer, you passed #{@read_timeout}"
|
32
|
-
end
|
33
|
-
|
34
32
|
ssl_set(*opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher))
|
33
|
+
|
34
|
+
if [:user,:pass,:hostname,:dbname,:db,:sock].any?{|k| @query_options.has_key?(k) }
|
35
|
+
warn "============= WARNING FROM mysql2 ============="
|
36
|
+
warn "The options :user, :pass, :hostname, :dbname, :db, and :sock will be deprecated at some point in the future."
|
37
|
+
warn "Instead, please use :username, :password, :host, 'localhost', :port, :database, :socket, :flags for the options."
|
38
|
+
warn "============= END WARNING FROM mysql2 ========="
|
39
|
+
end
|
35
40
|
|
36
|
-
user = opts[:username]
|
37
|
-
pass = opts[:password]
|
38
|
-
host = opts[:host] || 'localhost'
|
41
|
+
user = opts[:username] || opts[:user]
|
42
|
+
pass = opts[:password] || opts[:pass]
|
43
|
+
host = opts[:host] || opts[:hostname] || 'localhost'
|
39
44
|
port = opts[:port] || 3306
|
40
|
-
database = opts[:database]
|
41
|
-
socket = opts[:socket]
|
45
|
+
database = opts[:database] || opts[:dbname] || opts[:db]
|
46
|
+
socket = opts[:socket] || opts[:sock]
|
42
47
|
flags = opts[:flags] ? opts[:flags] | @query_options[:connect_flags] : @query_options[:connect_flags]
|
43
48
|
|
44
49
|
connect user, pass, host, port, database, socket, flags
|
@@ -87,6 +92,7 @@ module Mysql2
|
|
87
92
|
"ucs2" => Encoding::UTF_16BE,
|
88
93
|
"ujis" => Encoding::EucJP_ms,
|
89
94
|
"utf8" => Encoding::UTF_8,
|
95
|
+
"utf8mb4" => Encoding::UTF_8,
|
90
96
|
}
|
91
97
|
|
92
98
|
MYSQL_CHARSET_MAP = {
|
@@ -134,6 +140,8 @@ module Mysql2
|
|
134
140
|
42 => {:name => "latin7", :collation => "latin7_general_cs"},
|
135
141
|
43 => {:name => "macce", :collation => "macce_bin"},
|
136
142
|
44 => {:name => "cp1250", :collation => "cp1250_croatian_ci"},
|
143
|
+
45 => {:name => "utf8mb4", :collation => "utf8mb4_general_ci"},
|
144
|
+
46 => {:name => "utf8mb4", :collation => "utf8mb4_bin"},
|
137
145
|
47 => {:name => "latin1", :collation => "latin1_bin"},
|
138
146
|
48 => {:name => "latin1", :collation => "latin1_general_ci"},
|
139
147
|
49 => {:name => "latin1", :collation => "latin1_general_cs"},
|
@@ -218,6 +226,25 @@ module Mysql2
|
|
218
226
|
208 => {:name => "utf8", :collation => "utf8_persian_ci"},
|
219
227
|
209 => {:name => "utf8", :collation => "utf8_esperanto_ci"},
|
220
228
|
210 => {:name => "utf8", :collation => "utf8_hungarian_ci"},
|
229
|
+
224 => {:name => "utf8mb4", :collation => "utf8mb4_unicode_ci"},
|
230
|
+
225 => {:name => "utf8mb4", :collation => "utf8mb4_icelandic_ci"},
|
231
|
+
226 => {:name => "utf8mb4", :collation => "utf8mb4_latvian_ci"},
|
232
|
+
227 => {:name => "utf8mb4", :collation => "utf8mb4_romanian_ci"},
|
233
|
+
228 => {:name => "utf8mb4", :collation => "utf8mb4_slovenian_ci"},
|
234
|
+
229 => {:name => "utf8mb4", :collation => "utf8mb4_polish_ci"},
|
235
|
+
230 => {:name => "utf8mb4", :collation => "utf8mb4_estonian_ci"},
|
236
|
+
231 => {:name => "utf8mb4", :collation => "utf8mb4_spanish_ci"},
|
237
|
+
232 => {:name => "utf8mb4", :collation => "utf8mb4_swedish_ci"},
|
238
|
+
233 => {:name => "utf8mb4", :collation => "utf8mb4_turkish_ci"},
|
239
|
+
234 => {:name => "utf8mb4", :collation => "utf8mb4_czech_ci"},
|
240
|
+
235 => {:name => "utf8mb4", :collation => "utf8mb4_danish_ci"},
|
241
|
+
236 => {:name => "utf8mb4", :collation => "utf8mb4_lithuanian_ci"},
|
242
|
+
237 => {:name => "utf8mb4", :collation => "utf8mb4_slovak_ci"},
|
243
|
+
238 => {:name => "utf8mb4", :collation => "utf8mb4_spanish2_ci"},
|
244
|
+
239 => {:name => "utf8mb4", :collation => "utf8mb4_roman_ci"},
|
245
|
+
240 => {:name => "utf8mb4", :collation => "utf8mb4_persian_ci"},
|
246
|
+
241 => {:name => "utf8mb4", :collation => "utf8mb4_esperanto_ci"},
|
247
|
+
242 => {:name => "utf8mb4", :collation => "utf8mb4_hungarian_ci"},
|
221
248
|
254 => {:name => "utf8", :collation => "utf8_general_cs"}
|
222
249
|
}
|
223
250
|
|
data/lib/mysql2/em.rb
CHANGED
@@ -15,18 +15,28 @@ module Mysql2
|
|
15
15
|
def notify_readable
|
16
16
|
detach
|
17
17
|
begin
|
18
|
-
@
|
18
|
+
result = @client.async_result
|
19
19
|
rescue Exception => e
|
20
20
|
@deferable.fail(e)
|
21
|
+
else
|
22
|
+
@deferable.succeed(result)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
27
|
+
def close(*args)
|
28
|
+
if @watch
|
29
|
+
@watch.detach
|
30
|
+
end
|
31
|
+
super(*args)
|
32
|
+
end
|
33
|
+
|
25
34
|
def query(sql, opts={})
|
26
35
|
if ::EM.reactor_running?
|
27
36
|
super(sql, opts.merge(:async => true))
|
28
37
|
deferable = ::EM::DefaultDeferrable.new
|
29
|
-
::EM.watch(self.socket, Watcher, self, deferable)
|
38
|
+
@watch = ::EM.watch(self.socket, Watcher, self, deferable)
|
39
|
+
@watch.notify_readable = true
|
30
40
|
deferable
|
31
41
|
else
|
32
42
|
super(sql, opts)
|
@@ -34,4 +44,4 @@ module Mysql2
|
|
34
44
|
end
|
35
45
|
end
|
36
46
|
end
|
37
|
-
end
|
47
|
+
end
|
data/lib/mysql2/version.rb
CHANGED
data/spec/em/em_spec.rb
CHANGED
@@ -8,14 +8,14 @@ begin
|
|
8
8
|
it "should support async queries" do
|
9
9
|
results = []
|
10
10
|
EM.run do
|
11
|
-
client1 = Mysql2::EM::Client.new
|
11
|
+
client1 = Mysql2::EM::Client.new DatabaseCredentials['root']
|
12
12
|
defer1 = client1.query "SELECT sleep(0.1) as first_query"
|
13
13
|
defer1.callback do |result|
|
14
14
|
results << result.first
|
15
15
|
EM.stop_event_loop
|
16
16
|
end
|
17
17
|
|
18
|
-
client2 = Mysql2::EM::Client.new
|
18
|
+
client2 = Mysql2::EM::Client.new DatabaseCredentials['root']
|
19
19
|
defer2 = client2.query "SELECT sleep(0.025) second_query"
|
20
20
|
defer2.callback do |result|
|
21
21
|
results << result.first
|
@@ -29,7 +29,7 @@ begin
|
|
29
29
|
it "should support queries in callbacks" do
|
30
30
|
results = []
|
31
31
|
EM.run do
|
32
|
-
client = Mysql2::EM::Client.new
|
32
|
+
client = Mysql2::EM::Client.new DatabaseCredentials['root']
|
33
33
|
defer1 = client.query "SELECT sleep(0.025) as first_query"
|
34
34
|
defer1.callback do |result|
|
35
35
|
results << result.first
|
@@ -44,6 +44,66 @@ begin
|
|
44
44
|
results[0].keys.should include("first_query")
|
45
45
|
results[1].keys.should include("second_query")
|
46
46
|
end
|
47
|
+
|
48
|
+
it "should not swallow exceptions raised in callbacks" do
|
49
|
+
lambda {
|
50
|
+
EM.run do
|
51
|
+
client = Mysql2::EM::Client.new DatabaseCredentials['root']
|
52
|
+
defer = client.query "SELECT sleep(0.1) as first_query"
|
53
|
+
defer.callback do |result|
|
54
|
+
raise 'some error'
|
55
|
+
end
|
56
|
+
defer.errback do |err|
|
57
|
+
# This _shouldn't_ be run, but it needed to prevent the specs from
|
58
|
+
# freezing if this test fails.
|
59
|
+
EM.stop_event_loop
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}.should raise_error
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when an exception is raised by the client' do
|
66
|
+
let(:client) { Mysql2::EM::Client.new DatabaseCredentials['root'] }
|
67
|
+
let(:error) { StandardError.new('some error') }
|
68
|
+
before { client.stub(:async_result).and_raise(error) }
|
69
|
+
|
70
|
+
it "should swallow exceptions raised in by the client" do
|
71
|
+
errors = []
|
72
|
+
EM.run do
|
73
|
+
defer = client.query "SELECT sleep(0.1) as first_query"
|
74
|
+
defer.callback do |result|
|
75
|
+
# This _shouldn't_ be run, but it is needed to prevent the specs from
|
76
|
+
# freezing if this test fails.
|
77
|
+
EM.stop_event_loop
|
78
|
+
end
|
79
|
+
defer.errback do |err|
|
80
|
+
errors << err
|
81
|
+
EM.stop_event_loop
|
82
|
+
end
|
83
|
+
end
|
84
|
+
errors.should == [error]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should fail the deferrable" do
|
88
|
+
callbacks_run = []
|
89
|
+
EM.run do
|
90
|
+
defer = client.query "SELECT sleep(0.025) as first_query"
|
91
|
+
EM.add_timer(0.1) do
|
92
|
+
defer.callback do |result|
|
93
|
+
callbacks_run << :callback
|
94
|
+
# This _shouldn't_ be run, but it is needed to prevent the specs from
|
95
|
+
# freezing if this test fails.
|
96
|
+
EM.stop_event_loop
|
97
|
+
end
|
98
|
+
defer.errback do |err|
|
99
|
+
callbacks_run << :errback
|
100
|
+
EM.stop_event_loop
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
callbacks_run.should == [:errback]
|
105
|
+
end
|
106
|
+
end
|
47
107
|
end
|
48
108
|
rescue LoadError
|
49
109
|
puts "EventMachine not installed, skipping the specs that use it"
|