mysql2 0.3.11 → 0.3.12b1

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,10 +33,12 @@ void init_mysql2_client();
33
33
 
34
34
  typedef struct {
35
35
  VALUE encoding;
36
- int active;
36
+ VALUE active_thread; /* rb_thread_current() or Qnil */
37
37
  int reconnect_enabled;
38
- int closed;
38
+ int active;
39
+ int connected;
40
+ int initialized;
39
41
  MYSQL *client;
40
42
  } mysql_client_wrapper;
41
43
 
42
- #endif
44
+ #endif
@@ -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
- unless RUBY_PLATFORM =~ /mswin/ or RUBY_PLATFORM =~ /sparc/
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
- $LDFLAGS << " -Wl,-rpath,#{hard_mysql_path}"
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')
@@ -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] == '1' ? Qtrue : Qfalse;
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 (strtod(row[i], NULL) == 0.000000){
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
- wrapper->numberOfRows = mysql_num_rows(wrapper->result);
449
- if (wrapper->numberOfRows == 0) {
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
- return wrapper->rows;
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 (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
457
- // we've already read the entire dataset from the C result into our
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
- if (cacheRows && i < rowsProcessed) {
468
- row = rb_ary_entry(wrapper->rows, i);
469
- } else {
470
- row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast);
471
- if (cacheRows) {
472
- rb_ary_store(wrapper->rows, i, row);
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
- wrapper->lastRowProcessed++;
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
- if (row == Qnil) {
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
- return INT2FIX(mysql_num_rows(wrapper->result));
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
@@ -11,6 +11,7 @@ typedef struct {
11
11
  unsigned int numberOfFields;
12
12
  unsigned long numberOfRows;
13
13
  unsigned long lastRowProcessed;
14
+ char streamingComplete;
14
15
  char resultFreed;
15
16
  MYSQL_RES *result;
16
17
  } mysql2_result_wrapper;
@@ -16,6 +16,22 @@ module Mysql2
16
16
  end
17
17
 
18
18
  if defined?(ActiveRecord::VERSION::STRING) && ActiveRecord::VERSION::STRING < "3.1"
19
- puts "WARNING: This version of mysql2 (#{Mysql2::VERSION}) doesn't ship with the ActiveRecord adapter bundled anymore as it's now part of Rails 3.1"
20
- puts "WARNING: Please use the 0.2.x releases if you plan on using it in Rails <= 3.0.x"
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
@@ -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
- init_connection
21
+ initialize_ext
21
22
 
22
- [:reconnect, :connect_timeout].each do |key|
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
 
@@ -15,18 +15,28 @@ module Mysql2
15
15
  def notify_readable
16
16
  detach
17
17
  begin
18
- @deferable.succeed(@client.async_result)
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).notify_readable = true
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
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.3.11"
2
+ VERSION = "0.3.12b1"
3
3
  end
@@ -0,0 +1,11 @@
1
+ root:
2
+ host: localhost
3
+ username: root
4
+ password:
5
+ database: test
6
+
7
+ user:
8
+ host: localhost
9
+ username: LOCALUSERNAME
10
+ password:
11
+ database: mysql2_test
@@ -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"