mysql2 0.3.11 → 0.3.12b1
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|