sequel 3.28.0 → 3.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. data/CHANGELOG +119 -3
  2. data/Rakefile +5 -3
  3. data/bin/sequel +1 -5
  4. data/doc/model_hooks.rdoc +9 -1
  5. data/doc/opening_databases.rdoc +49 -40
  6. data/doc/prepared_statements.rdoc +27 -6
  7. data/doc/release_notes/3.28.0.txt +2 -2
  8. data/doc/release_notes/3.29.0.txt +459 -0
  9. data/doc/sharding.rdoc +7 -1
  10. data/doc/testing.rdoc +18 -9
  11. data/doc/transactions.rdoc +41 -1
  12. data/lib/sequel/adapters/ado.rb +28 -17
  13. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  14. data/lib/sequel/adapters/amalgalite.rb +11 -7
  15. data/lib/sequel/adapters/db2.rb +122 -70
  16. data/lib/sequel/adapters/dbi.rb +15 -15
  17. data/lib/sequel/adapters/do.rb +5 -36
  18. data/lib/sequel/adapters/do/mysql.rb +0 -5
  19. data/lib/sequel/adapters/do/postgres.rb +0 -5
  20. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  21. data/lib/sequel/adapters/firebird.rb +3 -6
  22. data/lib/sequel/adapters/ibmdb.rb +24 -16
  23. data/lib/sequel/adapters/informix.rb +2 -4
  24. data/lib/sequel/adapters/jdbc.rb +47 -11
  25. data/lib/sequel/adapters/jdbc/as400.rb +5 -24
  26. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  27. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  28. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/h2.rb +10 -12
  30. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  31. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  32. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  33. data/lib/sequel/adapters/jdbc/mysql.rb +0 -10
  34. data/lib/sequel/adapters/jdbc/oracle.rb +70 -3
  35. data/lib/sequel/adapters/jdbc/postgresql.rb +0 -11
  36. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  38. data/lib/sequel/adapters/jdbc/transactions.rb +56 -7
  39. data/lib/sequel/adapters/mock.rb +315 -0
  40. data/lib/sequel/adapters/mysql.rb +64 -51
  41. data/lib/sequel/adapters/mysql2.rb +15 -9
  42. data/lib/sequel/adapters/odbc.rb +13 -6
  43. data/lib/sequel/adapters/odbc/db2.rb +0 -4
  44. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  45. data/lib/sequel/adapters/openbase.rb +2 -4
  46. data/lib/sequel/adapters/oracle.rb +333 -51
  47. data/lib/sequel/adapters/postgres.rb +80 -27
  48. data/lib/sequel/adapters/shared/access.rb +0 -6
  49. data/lib/sequel/adapters/shared/db2.rb +13 -15
  50. data/lib/sequel/adapters/shared/firebird.rb +6 -6
  51. data/lib/sequel/adapters/shared/mssql.rb +23 -18
  52. data/lib/sequel/adapters/shared/mysql.rb +6 -6
  53. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  54. data/lib/sequel/adapters/shared/oracle.rb +185 -30
  55. data/lib/sequel/adapters/shared/postgres.rb +35 -18
  56. data/lib/sequel/adapters/shared/progress.rb +0 -6
  57. data/lib/sequel/adapters/shared/sqlite.rb +116 -37
  58. data/lib/sequel/adapters/sqlite.rb +16 -8
  59. data/lib/sequel/adapters/swift.rb +5 -5
  60. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  61. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  62. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  63. data/lib/sequel/adapters/tinytds.rb +13 -10
  64. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +8 -0
  65. data/lib/sequel/core.rb +40 -0
  66. data/lib/sequel/database/connecting.rb +1 -2
  67. data/lib/sequel/database/dataset.rb +3 -3
  68. data/lib/sequel/database/dataset_defaults.rb +58 -0
  69. data/lib/sequel/database/misc.rb +62 -2
  70. data/lib/sequel/database/query.rb +113 -49
  71. data/lib/sequel/database/schema_methods.rb +7 -2
  72. data/lib/sequel/dataset/actions.rb +37 -19
  73. data/lib/sequel/dataset/features.rb +24 -0
  74. data/lib/sequel/dataset/graph.rb +7 -6
  75. data/lib/sequel/dataset/misc.rb +11 -3
  76. data/lib/sequel/dataset/mutation.rb +2 -3
  77. data/lib/sequel/dataset/prepared_statements.rb +6 -4
  78. data/lib/sequel/dataset/query.rb +46 -15
  79. data/lib/sequel/dataset/sql.rb +28 -4
  80. data/lib/sequel/extensions/named_timezones.rb +5 -0
  81. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  82. data/lib/sequel/model.rb +2 -1
  83. data/lib/sequel/model/associations.rb +115 -33
  84. data/lib/sequel/model/base.rb +91 -31
  85. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  86. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  87. data/lib/sequel/plugins/force_encoding.rb +6 -6
  88. data/lib/sequel/plugins/identity_map.rb +1 -1
  89. data/lib/sequel/plugins/many_through_many.rb +6 -10
  90. data/lib/sequel/plugins/prepared_statements.rb +12 -1
  91. data/lib/sequel/plugins/prepared_statements_associations.rb +1 -1
  92. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  93. data/lib/sequel/plugins/serialization.rb +6 -1
  94. data/lib/sequel/plugins/sharding.rb +0 -5
  95. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  96. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  97. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  98. data/lib/sequel/timezones.rb +42 -42
  99. data/lib/sequel/version.rb +1 -1
  100. data/spec/adapters/mssql_spec.rb +29 -29
  101. data/spec/adapters/mysql_spec.rb +86 -104
  102. data/spec/adapters/oracle_spec.rb +48 -76
  103. data/spec/adapters/postgres_spec.rb +98 -33
  104. data/spec/adapters/spec_helper.rb +0 -5
  105. data/spec/adapters/sqlite_spec.rb +24 -21
  106. data/spec/core/connection_pool_spec.rb +9 -15
  107. data/spec/core/core_sql_spec.rb +20 -31
  108. data/spec/core/database_spec.rb +491 -227
  109. data/spec/core/dataset_spec.rb +638 -1051
  110. data/spec/core/expression_filters_spec.rb +0 -1
  111. data/spec/core/mock_adapter_spec.rb +378 -0
  112. data/spec/core/object_graph_spec.rb +48 -114
  113. data/spec/core/schema_generator_spec.rb +3 -3
  114. data/spec/core/schema_spec.rb +51 -114
  115. data/spec/core/spec_helper.rb +3 -90
  116. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  117. data/spec/extensions/dataset_associations_spec.rb +199 -0
  118. data/spec/extensions/instance_hooks_spec.rb +71 -0
  119. data/spec/extensions/named_timezones_spec.rb +22 -2
  120. data/spec/extensions/nested_attributes_spec.rb +3 -0
  121. data/spec/extensions/schema_spec.rb +1 -1
  122. data/spec/extensions/serialization_modification_detection_spec.rb +1 -0
  123. data/spec/extensions/serialization_spec.rb +5 -8
  124. data/spec/extensions/spec_helper.rb +4 -0
  125. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  126. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  127. data/spec/integration/associations_test.rb +123 -12
  128. data/spec/integration/dataset_test.rb +140 -47
  129. data/spec/integration/eager_loader_test.rb +19 -21
  130. data/spec/integration/model_test.rb +80 -1
  131. data/spec/integration/plugin_test.rb +179 -128
  132. data/spec/integration/prepared_statement_test.rb +92 -91
  133. data/spec/integration/schema_test.rb +42 -23
  134. data/spec/integration/spec_helper.rb +25 -31
  135. data/spec/integration/timezone_test.rb +38 -12
  136. data/spec/integration/transaction_test.rb +161 -34
  137. data/spec/integration/type_test.rb +3 -3
  138. data/spec/model/association_reflection_spec.rb +83 -7
  139. data/spec/model/associations_spec.rb +393 -676
  140. data/spec/model/base_spec.rb +186 -116
  141. data/spec/model/dataset_methods_spec.rb +7 -27
  142. data/spec/model/eager_loading_spec.rb +343 -867
  143. data/spec/model/hooks_spec.rb +160 -79
  144. data/spec/model/model_spec.rb +118 -165
  145. data/spec/model/plugins_spec.rb +7 -13
  146. data/spec/model/record_spec.rb +138 -207
  147. data/spec/model/spec_helper.rb +10 -73
  148. metadata +14 -8
@@ -144,13 +144,6 @@ describe "An SQLite dataset" do
144
144
  @d = SQLITE_DB[:items]
145
145
  end
146
146
 
147
- specify "should handle string pattern matches correctly" do
148
- @d.literal(:x.like('a')).should == "(x LIKE 'a')"
149
- @d.literal(~:x.like('a')).should == "NOT (x LIKE 'a')"
150
- @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
151
- @d.literal(~:x.ilike('a')).should == "NOT (x LIKE 'a')"
152
- end
153
-
154
147
  specify "should raise errors if given a regexp pattern match" do
155
148
  proc{@d.literal(:x.like(/a/))}.should raise_error(Sequel::Error)
156
149
  proc{@d.literal(~:x.like(/a/))}.should raise_error(Sequel::Error)
@@ -175,27 +168,27 @@ end
175
168
 
176
169
  describe "An SQLite dataset AS clause" do
177
170
  specify "should use a string literal for :col___alias" do
178
- SQLITE_DB.literal(:c___a).should == "c AS 'a'"
171
+ SQLITE_DB.literal(:c___a).should == "`c` AS 'a'"
179
172
  end
180
173
 
181
174
  specify "should use a string literal for :table__col___alias" do
182
- SQLITE_DB.literal(:t__c___a).should == "t.c AS 'a'"
175
+ SQLITE_DB.literal(:t__c___a).should == "`t`.`c` AS 'a'"
183
176
  end
184
177
 
185
178
  specify "should use a string literal for :column.as(:alias)" do
186
- SQLITE_DB.literal(:c.as(:a)).should == "c AS 'a'"
179
+ SQLITE_DB.literal(:c.as(:a)).should == "`c` AS 'a'"
187
180
  end
188
181
 
189
182
  specify "should use a string literal in the SELECT clause" do
190
- SQLITE_DB[:t].select(:c___a).sql.should == "SELECT c AS 'a' FROM t"
183
+ SQLITE_DB[:t].select(:c___a).sql.should == "SELECT `c` AS 'a' FROM `t`"
191
184
  end
192
185
 
193
186
  specify "should use a string literal in the FROM clause" do
194
- SQLITE_DB[:t___a].sql.should == "SELECT * FROM t AS 'a'"
187
+ SQLITE_DB[:t___a].sql.should == "SELECT * FROM `t` AS 'a'"
195
188
  end
196
189
 
197
190
  specify "should use a string literal in the JOIN clause" do
198
- SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM t NATURAL JOIN j AS 'a'"
191
+ SQLITE_DB[:t].join_table(:natural, :j, nil, :a).sql.should == "SELECT * FROM `t` NATURAL JOIN `j` AS 'a'"
199
192
  end
200
193
  end
201
194
 
@@ -324,6 +317,15 @@ describe "A SQLite database" do
324
317
  @db[:test2].first.should == {:name => 'mmm'}
325
318
  end
326
319
 
320
+ specify "should keep a composite primary key when dropping columns" do
321
+ @db.create_table!(:test2){Integer :a; Integer :b; Integer :c; primary_key [:a, :b]}
322
+ @db.drop_column :test2, :c
323
+ @db[:test2].columns.should == [:a, :b]
324
+ @db[:test2] << {:a=>1, :b=>2}
325
+ @db[:test2] << {:a=>2, :b=>3}
326
+ proc{@db[:test2] << {:a=>2, :b=>3}}.should raise_error(Sequel::Error)
327
+ end
328
+
327
329
  specify "should keep column attributes when dropping a column" do
328
330
  @db.create_table! :test3 do
329
331
  primary_key :id
@@ -423,14 +425,15 @@ describe "A SQLite database" do
423
425
  specify "should choose a temporary table name that isn't already used when dropping or renaming columns" do
424
426
  sqls = []
425
427
  @db.loggers << (l=Class.new{%w'info error'.each{|m| define_method(m){|sql| sqls << sql}}}.new)
426
- @db.create_table! :test3 do
428
+ @db.tables.each{|t| @db.drop_table(t) if t.to_s =~ /test3/}
429
+ @db.create_table :test3 do
427
430
  Integer :h
428
431
  Integer :i
429
432
  end
430
- @db.create_table! :test3_backup0 do
433
+ @db.create_table :test3_backup0 do
431
434
  Integer :j
432
435
  end
433
- @db.create_table! :test3_backup1 do
436
+ @db.create_table :test3_backup1 do
434
437
  Integer :k
435
438
  end
436
439
 
@@ -439,20 +442,20 @@ describe "A SQLite database" do
439
442
  @db[:test3_backup1].columns.should == [:k]
440
443
  sqls.clear
441
444
  @db.drop_column(:test3, :i)
442
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup2/}.should == true
443
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup[01]/}.should == false
445
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup2/}.should == true
446
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup[01]/}.should == false
444
447
  @db[:test3].columns.should == [:h]
445
448
  @db[:test3_backup0].columns.should == [:j]
446
449
  @db[:test3_backup1].columns.should == [:k]
447
450
 
448
- @db.create_table! :test3_backup2 do
451
+ @db.create_table :test3_backup2 do
449
452
  Integer :l
450
453
  end
451
454
 
452
455
  sqls.clear
453
456
  @db.rename_column(:test3, :h, :i)
454
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup3/}.should == true
455
- sqls.any?{|x| x =~ /\ACREATE TABLE.*test3_backup[012]/}.should == false
457
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup3/}.should == true
458
+ sqls.any?{|x| x =~ /\AALTER TABLE.*test3.*RENAME TO.*test3_backup[012]/}.should == false
456
459
  @db[:test3].columns.should == [:i]
457
460
  @db[:test3_backup0].columns.should == [:j]
458
461
  @db[:test3_backup1].columns.should == [:k]
@@ -124,36 +124,30 @@ describe "A connection pool handling connection errors" do
124
124
  end
125
125
  end
126
126
 
127
- class DummyConnection
128
- @@value = 0
129
- def initialize
130
- @@value += 1
131
- end
132
-
133
- def value
134
- @@value
135
- end
136
- end
137
-
138
127
  describe "ConnectionPool#hold" do
139
128
  before do
140
- @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
129
+ value = 0
130
+ @c = Class.new do
131
+ define_method(:initialize){value += 1}
132
+ define_method(:value){value}
133
+ end
134
+ @pool = Sequel::ConnectionPool.get_pool(CONNECTION_POOL_DEFAULTS){@c.new}
141
135
  end
142
136
 
143
137
  specify "should pass the result of the connection maker proc to the supplied block" do
144
138
  res = nil
145
139
  @pool.hold {|c| res = c}
146
- res.should be_a_kind_of(DummyConnection)
140
+ res.should be_a_kind_of(@c)
147
141
  res.value.should == 1
148
142
  @pool.hold {|c| res = c}
149
- res.should be_a_kind_of(DummyConnection)
143
+ res.should be_a_kind_of(@c)
150
144
  res.value.should == 1 # the connection maker is invoked only once
151
145
  end
152
146
 
153
147
  specify "should be re-entrant by the same thread" do
154
148
  cc = nil
155
149
  @pool.hold {|c| @pool.hold {|c| @pool.hold {|c| cc = c}}}
156
- cc.should be_a_kind_of(DummyConnection)
150
+ cc.should be_a_kind_of(@c)
157
151
  end
158
152
 
159
153
  specify "should catch exceptions and reraise them" do
@@ -75,16 +75,16 @@ end
75
75
 
76
76
  describe "String#lit" do
77
77
  before do
78
- @ds = ds = MockDatabase.new.dataset
78
+ @ds = ds = Sequel::Database.new[:t]
79
79
  end
80
+
80
81
  specify "should return an LiteralString object" do
81
82
  'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
82
83
  'xyz'.lit.to_s.should == 'xyz'
83
84
  end
84
85
 
85
86
  specify "should inhibit string literalization" do
86
- Sequel::Database.new[:t].update_sql(:stamp => "NOW()".lit).should == \
87
- "UPDATE t SET stamp = NOW()"
87
+ @ds.update_sql(:stamp => "NOW()".lit).should == "UPDATE t SET stamp = NOW()"
88
88
  end
89
89
 
90
90
  specify "should return a PlaceholderLiteralString object if args are given" do
@@ -176,10 +176,8 @@ end
176
176
 
177
177
  describe "Column references" do
178
178
  before do
179
- @c = Class.new(Sequel::Dataset) do
180
- def quoted_identifier(c); "`#{c}`"; end
181
- end
182
- @ds = @c.new(MockDatabase.new)
179
+ @ds = Sequel::Database.new.dataset
180
+ def @ds.quoted_identifier(c); "`#{c}`"; end
183
181
  @ds.quote_identifiers = true
184
182
  end
185
183
 
@@ -289,7 +287,7 @@ end
289
287
 
290
288
  describe "Dataset#literal" do
291
289
  before do
292
- @ds = MockDataset.new(nil)
290
+ @ds = Sequel::Database.new.dataset
293
291
  end
294
292
 
295
293
  specify "should convert qualified symbol notation into dot notation" do
@@ -325,17 +323,12 @@ end
325
323
 
326
324
  describe "Symbol" do
327
325
  before do
328
- @ds = Sequel::Dataset.new(MockDatabase.new)
326
+ @ds = Sequel::Database.new.dataset
329
327
  end
330
328
 
331
- specify "should support upper case outer functions" do
329
+ specify "should support sql_function method" do
332
330
  :COUNT.sql_function('1').to_s(@ds).should == "COUNT('1')"
333
- end
334
-
335
- specify "should inhibit string literalization" do
336
- db = Sequel::Database.new
337
- ds = db[:t]
338
- ds.select(:COUNT.sql_function('1')).sql.should == "SELECT COUNT('1') FROM t"
331
+ @ds.select(:COUNT.sql_function('1')).sql.should == "SELECT COUNT('1')"
339
332
  end
340
333
 
341
334
  specify "should support cast method" do
@@ -367,22 +360,18 @@ describe "Symbol" do
367
360
  end
368
361
 
369
362
  specify "should allow database independent types when casting" do
370
- m = MockDatabase.new
371
- m.instance_eval do
372
- def cast_type_literal(type)
373
- return :foo if type == Integer
374
- return :bar if type == String
375
- type
376
- end
363
+ db = @ds.db
364
+ def db.cast_type_literal(type)
365
+ return :foo if type == Integer
366
+ return :bar if type == String
367
+ type
377
368
  end
378
- @ds2 = Sequel::Dataset.new(m)
379
- :abc.cast(String).to_s(@ds).should == "CAST(abc AS varchar(255))"
380
- :abc.cast(String).to_s(@ds2).should == "CAST(abc AS bar)"
381
- :abc.cast(String).to_s(@ds2).should == "CAST(abc AS bar)"
382
- :abc.cast_string.to_s(@ds2).should == "CAST(abc AS bar)"
383
- :abc.cast_string(Integer).to_s(@ds2).should == "CAST(abc AS foo)"
384
- :abc.cast_numeric.to_s(@ds2).should == "CAST(abc AS foo)"
385
- :abc.cast_numeric(String).to_s(@ds2).should == "CAST(abc AS bar)"
369
+ :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
370
+ :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
371
+ :abc.cast_string.to_s(@ds).should == "CAST(abc AS bar)"
372
+ :abc.cast_string(Integer).to_s(@ds).should == "CAST(abc AS foo)"
373
+ :abc.cast_numeric.to_s(@ds).should == "CAST(abc AS foo)"
374
+ :abc.cast_numeric(String).to_s(@ds).should == "CAST(abc AS bar)"
386
375
  end
387
376
 
388
377
  specify "should support SQL EXTRACT function via #extract " do
@@ -74,46 +74,46 @@ describe "A new Database" do
74
74
  Sequel.identifier_input_method = nil
75
75
  Sequel::Database.identifier_input_method.should == ""
76
76
  db = Sequel::Database.new(:identifier_input_method=>nil)
77
- db.identifier_input_method.should == nil
77
+ db.identifier_input_method.should be_nil
78
78
  db.identifier_input_method = :downcase
79
79
  db.identifier_input_method.should == :downcase
80
80
  db = Sequel::Database.new(:identifier_input_method=>:upcase)
81
81
  db.identifier_input_method.should == :upcase
82
82
  db.identifier_input_method = nil
83
- db.identifier_input_method.should == nil
83
+ db.identifier_input_method.should be_nil
84
84
  Sequel.identifier_input_method = :downcase
85
85
  Sequel::Database.identifier_input_method.should == :downcase
86
86
  db = Sequel::Database.new(:identifier_input_method=>nil)
87
- db.identifier_input_method.should == nil
87
+ db.identifier_input_method.should be_nil
88
88
  db.identifier_input_method = :upcase
89
89
  db.identifier_input_method.should == :upcase
90
90
  db = Sequel::Database.new(:identifier_input_method=>:upcase)
91
91
  db.identifier_input_method.should == :upcase
92
92
  db.identifier_input_method = nil
93
- db.identifier_input_method.should == nil
93
+ db.identifier_input_method.should be_nil
94
94
  end
95
95
 
96
96
  specify "should respect the :identifier_output_method option" do
97
97
  Sequel.identifier_output_method = nil
98
98
  Sequel::Database.identifier_output_method.should == ""
99
99
  db = Sequel::Database.new(:identifier_output_method=>nil)
100
- db.identifier_output_method.should == nil
100
+ db.identifier_output_method.should be_nil
101
101
  db.identifier_output_method = :downcase
102
102
  db.identifier_output_method.should == :downcase
103
103
  db = Sequel::Database.new(:identifier_output_method=>:upcase)
104
104
  db.identifier_output_method.should == :upcase
105
105
  db.identifier_output_method = nil
106
- db.identifier_output_method.should == nil
106
+ db.identifier_output_method.should be_nil
107
107
  Sequel.identifier_output_method = :downcase
108
108
  Sequel::Database.identifier_output_method.should == :downcase
109
109
  db = Sequel::Database.new(:identifier_output_method=>nil)
110
- db.identifier_output_method.should == nil
110
+ db.identifier_output_method.should be_nil
111
111
  db.identifier_output_method = :upcase
112
112
  db.identifier_output_method.should == :upcase
113
113
  db = Sequel::Database.new(:identifier_output_method=>:upcase)
114
114
  db.identifier_output_method.should == :upcase
115
115
  db.identifier_output_method = nil
116
- db.identifier_output_method.should == nil
116
+ db.identifier_output_method.should be_nil
117
117
  end
118
118
 
119
119
  specify "should use the default Sequel.quote_identifiers value" do
@@ -384,10 +384,85 @@ describe "Database#dataset" do
384
384
  end
385
385
  end
386
386
 
387
+ describe "Database#dataset_class" do
388
+ before do
389
+ @db = Sequel::Database.new
390
+ @dsc = Class.new(Sequel::Dataset)
391
+ end
392
+
393
+ specify "should have setter set the class to use to create datasets" do
394
+ @db.dataset_class = @dsc
395
+ ds = @db.dataset
396
+ ds.should be_a_kind_of(@dsc)
397
+ ds.opts.should == {}
398
+ ds.db.should be(@db)
399
+ end
400
+
401
+ specify "should have getter return the class to use to create datasets" do
402
+ @db.dataset_class.should == Sequel::Dataset
403
+ @db.dataset_class = @dsc
404
+ @db.dataset_class.should == @dsc
405
+ end
406
+ end
407
+
408
+ describe "Database#extend_datasets" do
409
+ before do
410
+ @db = Sequel::Database.new
411
+ @m = Module.new{def foo() [3] end}
412
+ @m2 = Module.new{def foo() [4] + super end}
413
+ @db.extend_datasets(@m)
414
+ end
415
+
416
+ specify "should change the dataset class to a subclass the first time it is called" do
417
+ @db.dataset_class.superclass.should == Sequel::Dataset
418
+ end
419
+
420
+ specify "should not create a subclass of the dataset class if called more than once" do
421
+ @db.extend_datasets(@m2)
422
+ @db.dataset_class.superclass.should == Sequel::Dataset
423
+ end
424
+
425
+ specify "should make the dataset class include the module" do
426
+ @db.dataset_class.ancestors.should include(@m)
427
+ @db.dataset_class.ancestors.should_not include(@m2)
428
+ @db.extend_datasets(@m2)
429
+ @db.dataset_class.ancestors.should include(@m)
430
+ @db.dataset_class.ancestors.should include(@m2)
431
+ end
432
+
433
+ specify "should have datasets respond to the module's methods" do
434
+ @db.dataset.foo.should == [3]
435
+ @db.extend_datasets(@m2)
436
+ @db.dataset.foo.should == [4, 3]
437
+ end
438
+
439
+ specify "should take a block and create a module from it to use" do
440
+ @db.dataset.foo.should == [3]
441
+ @db.extend_datasets{def foo() [5] + super end}
442
+ @db.dataset.foo.should == [5, 3]
443
+ end
444
+
445
+ specify "should raise an error if both a module and a block are provided" do
446
+ proc{@db.extend_datasets(@m2){def foo() [5] + super end}}.should raise_error(Sequel::Error)
447
+ end
448
+
449
+ specify "should be able to override methods defined in the original Dataset class" do
450
+ @db.extend_datasets(Module.new{def select(*a, &block) super.order(*a, &block) end})
451
+ @db[:t].select(:a, :b).sql.should == 'SELECT a, b FROM t ORDER BY a, b'
452
+ end
453
+
454
+ specify "should reapply settings if dataset_class is chagned" do
455
+ c = Class.new(Sequel::Dataset)
456
+ @db.dataset_class = c
457
+ @db.dataset_class.superclass.should == c
458
+ @db.dataset_class.ancestors.should include(@m)
459
+ @db.dataset.foo.should == [3]
460
+ end
461
+ end
462
+
387
463
  describe "Database#execute" do
388
464
  specify "should raise Sequel::NotImplemented" do
389
465
  proc {Sequel::Database.new.execute('blah blah')}.should raise_error(Sequel::NotImplemented)
390
- proc {Sequel::Database.new << 'blah blah'}.should raise_error(Sequel::NotImplemented)
391
466
  end
392
467
  end
393
468
 
@@ -409,30 +484,39 @@ describe "Database#indexes" do
409
484
  end
410
485
  end
411
486
 
412
- describe "Database#<< and run" do
487
+ describe "Database#run" do
413
488
  before do
414
- sqls = @sqls = []
415
- @c = Class.new(Sequel::Database) do
416
- define_method(:execute_ddl){|sql, *opts| sqls.clear; sqls << sql; sqls.concat(opts)}
417
- end
418
- @db = @c.new({})
489
+ @db = Sequel.mock(:servers=>{:s1=>{}})
419
490
  end
420
491
 
421
- specify "should pass the supplied sql to #execute_ddl" do
422
- (@db << "DELETE FROM items")
423
- @sqls.should == ["DELETE FROM items", {}]
424
- @db.run("DELETE FROM items2")
425
- @sqls.should == ["DELETE FROM items2", {}]
492
+ specify "should execute the code on the database" do
493
+ @db.run("DELETE FROM items")
494
+ @db.sqls.should == ["DELETE FROM items"]
426
495
  end
427
496
 
428
497
  specify "should return nil" do
429
- (@db << "DELETE FROM items").should == nil
430
- @db.run("DELETE FROM items").should == nil
498
+ @db.run("DELETE FROM items").should be_nil
431
499
  end
432
500
 
433
501
  specify "should accept options passed to execute_ddl" do
434
502
  @db.run("DELETE FROM items", :server=>:s1)
435
- @sqls.should == ["DELETE FROM items", {:server=>:s1}]
503
+ @db.sqls.should == ["DELETE FROM items -- s1"]
504
+ end
505
+ end
506
+
507
+ describe "Database#<<" do
508
+ before do
509
+ @db = Sequel.mock
510
+ end
511
+
512
+ specify "should execute the code on the database" do
513
+ @db << "DELETE FROM items"
514
+ @db.sqls.should == ["DELETE FROM items"]
515
+ end
516
+
517
+ specify "should be chainable" do
518
+ @db << "DELETE FROM items" << "DELETE FROM items2"
519
+ @db.sqls.should == ["DELETE FROM items", "DELETE FROM items2"]
436
520
  end
437
521
  end
438
522
 
@@ -442,19 +526,19 @@ describe "Database#synchronize" do
442
526
  end
443
527
 
444
528
  specify "should wrap the supplied block in pool.hold" do
445
- stop = false
529
+ q, q1, q2, q3 = Queue.new, Queue.new, Queue.new, Queue.new
446
530
  c1, c2 = nil
447
- t1 = Thread.new {@db.synchronize {|c| c1 = c; while !stop;sleep 0.1;end}}
448
- while !c1;end
531
+ t1 = Thread.new{@db.synchronize{|c| c1 = c; q.push nil; q1.pop}; q.push nil}
532
+ q.pop
449
533
  c1.should == 12345
450
- t2 = Thread.new {@db.synchronize {|c| c2 = c}}
451
- sleep 0.2
534
+ t2 = Thread.new{@db.synchronize{|c| c2 = c; q2.push nil}}
452
535
  @db.pool.available_connections.should be_empty
453
536
  c2.should be_nil
454
- stop = true
455
- t1.join
456
- sleep 0.1
537
+ q1.push nil
538
+ q.pop
539
+ q2.pop
457
540
  c2.should == 12345
541
+ t1.join
458
542
  t2.join
459
543
  end
460
544
  end
@@ -464,7 +548,7 @@ describe "Database#test_connection" do
464
548
  @db = Sequel::Database.new{@test = rand(100)}
465
549
  end
466
550
 
467
- specify "should pool#hold" do
551
+ specify "should attempt to get a connection" do
468
552
  @db.test_connection
469
553
  @test.should_not be_nil
470
554
  end
@@ -480,30 +564,21 @@ end
480
564
 
481
565
  describe "Database#table_exists?" do
482
566
  specify "should try to select the first record from the table's dataset" do
483
- db2 = DummyDatabase.new
484
- db2.table_exists?(:a).should be_false
485
- db2.table_exists?(:b).should be_true
486
- end
487
- end
488
-
489
- class Dummy3Database < Sequel::Database
490
- attr_reader :sql, :transactions
491
- def execute(sql, opts={}); @sql ||= []; @sql << sql; end
492
-
493
- class DummyConnection
494
- def initialize(db); @db = db; end
495
- def execute(sql); @db.execute(sql); end
567
+ db = Sequel.mock(:fetch=>[Sequel::Error, [], [{:a=>1}]])
568
+ db.table_exists?(:a).should be_false
569
+ db.table_exists?(:b).should be_true
570
+ db.table_exists?(:c).should be_true
496
571
  end
497
572
  end
498
573
 
499
574
  describe "Database#transaction" do
500
575
  before do
501
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
576
+ @db = Sequel.mock(:servers=>{:test=>{}})
502
577
  end
503
578
 
504
579
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
505
580
  @db.transaction{@db.execute 'DROP TABLE test;'}
506
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
581
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
507
582
  end
508
583
 
509
584
  specify "should support transaction isolation levels" do
@@ -511,7 +586,7 @@ describe "Database#transaction" do
511
586
  [:uncommitted, :committed, :repeatable, :serializable].each do |l|
512
587
  @db.transaction(:isolation=>l){@db.run "DROP TABLE #{l}"}
513
588
  end
514
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
589
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
515
590
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
516
591
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
517
592
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -523,7 +598,7 @@ describe "Database#transaction" do
523
598
  @db.transaction_isolation_level = l
524
599
  @db.transaction{@db.run "DROP TABLE #{l}"}
525
600
  end
526
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
601
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
527
602
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
528
603
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
529
604
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -538,12 +613,12 @@ describe "Database#transaction" do
538
613
  end
539
614
  end
540
615
  @db.ret_commit
541
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
616
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
542
617
  end
543
618
 
544
619
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
545
620
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
546
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
621
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
547
622
 
548
623
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
549
624
  end
@@ -555,7 +630,50 @@ describe "Database#transaction" do
555
630
  @db.drop_table(:b)
556
631
  end
557
632
 
558
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
633
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
634
+ end
635
+
636
+ specify "should have in_transaction? return true if inside a transaction" do
637
+ c = nil
638
+ @db.transaction{c = @db.in_transaction?}
639
+ c.should be_true
640
+ end
641
+
642
+ specify "should have in_transaction? handle sharding correctly" do
643
+ c = []
644
+ @db.transaction(:server=>:test){c << @db.in_transaction?}
645
+ @db.transaction(:server=>:test){c << @db.in_transaction?(:server=>:test)}
646
+ c.should == [false, true]
647
+ end
648
+
649
+ specify "should have in_transaction? return false if not in a transaction" do
650
+ @db.in_transaction?.should be_false
651
+ end
652
+
653
+ specify "should return nil if Sequel::Rollback is called in the transaction" do
654
+ @db.transaction{raise Sequel::Rollback}.should be_nil
655
+ end
656
+
657
+ specify "should reraise Sequel::Rollback errors when using :rollback=>:reraise option is given" do
658
+ proc {@db.transaction(:rollback=>:reraise){raise Sequel::Rollback}}.should raise_error(Sequel::Rollback)
659
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
660
+ proc {@db.transaction(:rollback=>:reraise){raise ArgumentError}}.should raise_error(ArgumentError)
661
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
662
+ @db.transaction(:rollback=>:reraise){1}.should == 1
663
+ @db.sqls.should == ['BEGIN', 'COMMIT']
664
+ end
665
+
666
+ specify "should always rollback if :rollback=>:always option is given" do
667
+ proc {@db.transaction(:rollback=>:always){raise ArgumentError}}.should raise_error(ArgumentError)
668
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
669
+ @db.transaction(:rollback=>:always){raise Sequel::Rollback}.should be_nil
670
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
671
+ @db.transaction(:rollback=>:always){1}.should be_nil
672
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
673
+ catch (:foo) do
674
+ @db.transaction(:rollback=>:always){throw :foo}
675
+ end
676
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
559
677
  end
560
678
 
561
679
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -567,22 +685,36 @@ describe "Database#transaction" do
567
685
  end
568
686
 
569
687
  specify "should be re-entrant" do
570
- stop = false
688
+ q, q1 = Queue.new, Queue.new
571
689
  cc = nil
572
690
  t = Thread.new do
573
691
  @db.transaction {@db.transaction {@db.transaction {|c|
574
692
  cc = c
575
- while !stop; sleep 0.1; end
693
+ q.pop
694
+ q1.push nil
695
+ q.pop
576
696
  }}}
577
697
  end
578
- while cc.nil?; sleep 0.1; end
579
- cc.should be_a_kind_of(Dummy3Database::DummyConnection)
580
- @db.transactions.should == [t]
581
- stop = true
698
+ q.push nil
699
+ q1.pop
700
+ cc.should be_a_kind_of(Sequel::Mock::Connection)
701
+ tr = @db.instance_variable_get(:@transactions)
702
+ tr.should == {cc=>{:savepoint_level=>1}}
703
+ q.push nil
582
704
  t.join
583
- @db.transactions.should be_empty
705
+ tr.should be_empty
584
706
  end
585
707
 
708
+ specify "should correctly handle nested transacation use with separate shards" do
709
+ @db.transaction do |c1|
710
+ @db.transaction(:server=>:test) do |c2|
711
+ c1.should_not == c2
712
+ @db.execute 'DROP TABLE test;'
713
+ end
714
+ end
715
+ @db.sqls.should == ['BEGIN', 'BEGIN -- test', 'DROP TABLE test;', 'COMMIT -- test', 'COMMIT']
716
+ end
717
+
586
718
  if (!defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
587
719
  specify "should handle Thread#kill for transactions inside threads" do
588
720
  q = Queue.new
@@ -598,35 +730,173 @@ describe "Database#transaction" do
598
730
  q1.pop
599
731
  t.kill
600
732
  t.join
601
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
733
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
602
734
  end
603
735
  end
736
+
737
+ specify "should raise an Error if after_commit or after_rollback is called without a block" do
738
+ proc{@db.after_commit}.should raise_error(Sequel::Error)
739
+ proc{@db.after_rollback}.should raise_error(Sequel::Error)
740
+ end
741
+
742
+ specify "should execute after_commit outside transactions" do
743
+ @db.after_commit{@db.execute('foo')}
744
+ @db.sqls.should == ['foo']
745
+ end
746
+
747
+ specify "should ignore after_rollback outside transactions" do
748
+ @db.after_rollback{@db.execute('foo')}
749
+ @db.sqls.should == []
750
+ end
751
+
752
+ specify "should support after_commit inside transactions" do
753
+ @db.transaction{@db.after_commit{@db.execute('foo')}}
754
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
755
+ end
756
+
757
+ specify "should support after_rollback inside transactions" do
758
+ @db.transaction{@db.after_rollback{@db.execute('foo')}}
759
+ @db.sqls.should == ['BEGIN', 'COMMIT']
760
+ end
761
+
762
+ specify "should not call after_commit if the transaction rolls back" do
763
+ @db.transaction{@db.after_commit{@db.execute('foo')}; raise Sequel::Rollback}
764
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
765
+ end
766
+
767
+ specify "should call after_rollback if the transaction rolls back" do
768
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; raise Sequel::Rollback}
769
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
770
+ end
771
+
772
+ specify "should call multiple after_commit blocks in order if called inside transactions" do
773
+ @db.transaction{@db.after_commit{@db.execute('foo')}; @db.after_commit{@db.execute('bar')}}
774
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo', 'bar']
775
+ end
776
+
777
+ specify "should call multiple after_rollback blocks in order if called inside transactions" do
778
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; @db.after_rollback{@db.execute('bar')}; raise Sequel::Rollback}
779
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo', 'bar']
780
+ end
781
+
782
+ specify "should support after_commit inside nested transactions" do
783
+ @db.transaction{@db.transaction{@db.after_commit{@db.execute('foo')}}}
784
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
785
+ end
786
+
787
+ specify "should support after_rollback inside nested transactions" do
788
+ @db.transaction{@db.transaction{@db.after_rollback{@db.execute('foo')}}; raise Sequel::Rollback}
789
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
790
+ end
791
+
792
+ specify "should support after_commit inside savepoints" do
793
+ @db.meta_def(:supports_savepoints?){true}
794
+ @db.transaction do
795
+ @db.after_commit{@db.execute('foo')}
796
+ @db.transaction(:savepoint=>true){@db.after_commit{@db.execute('bar')}}
797
+ @db.after_commit{@db.execute('baz')}
798
+ end
799
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar', 'baz']
800
+ end
801
+
802
+ specify "should support after_rollback inside savepoints" do
803
+ @db.meta_def(:supports_savepoints?){true}
804
+ @db.transaction do
805
+ @db.after_rollback{@db.execute('foo')}
806
+ @db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
807
+ @db.after_rollback{@db.execute('baz')}
808
+ raise Sequel::Rollback
809
+ end
810
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
811
+ end
812
+
813
+ specify "should raise an error if you attempt to use after_commit inside a prepared transaction" do
814
+ @db.meta_def(:supports_prepared_transactions?){true}
815
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_commit{@db.execute('foo')}}}.should raise_error(Sequel::Error)
816
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
817
+ end
818
+
819
+ specify "should raise an error if you attempt to use after_rollback inside a prepared transaction" do
820
+ @db.meta_def(:supports_prepared_transactions?){true}
821
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_rollback{@db.execute('foo')}}}.should raise_error(Sequel::Error)
822
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
823
+ end
824
+
825
+ specify "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
826
+ @db.meta_def(:supports_savepoints?){true}
827
+ @db.meta_def(:supports_prepared_transactions?){true}
828
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
829
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
830
+ end
831
+
832
+ specify "should raise an error if you attempt to use after_rollback inside a savepoint in a prepared transaction" do
833
+ @db.meta_def(:supports_savepoints?){true}
834
+ @db.meta_def(:supports_prepared_transactions?){true}
835
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
836
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
837
+ end
604
838
  end
605
839
 
840
+ describe "Sequel.transaction" do
841
+ before do
842
+ @sqls = []
843
+ @db1 = Sequel.mock(:append=>'1', :sqls=>@sqls)
844
+ @db2 = Sequel.mock(:append=>'2', :sqls=>@sqls)
845
+ @db3 = Sequel.mock(:append=>'3', :sqls=>@sqls)
846
+ end
847
+
848
+ specify "should run the block inside transacitons on all three databases" do
849
+ Sequel.transaction([@db1, @db2, @db3]){1}.should == 1
850
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
851
+ end
852
+
853
+ specify "should pass options to all the blocks" do
854
+ Sequel.transaction([@db1, @db2, @db3], :rollback=>:always){1}.should be_nil
855
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
856
+ end
857
+
858
+ specify "should handle Sequel::Rollback exceptions raised by the block to rollback on all databases" do
859
+ Sequel.transaction([@db1, @db2, @db3]){raise Sequel::Rollback}.should be_nil
860
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
861
+ end
862
+
863
+ specify "should handle nested transactions" do
864
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3]){1}}.should == 1
865
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
866
+ end
867
+
868
+ specify "should handle savepoints" do
869
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3], :savepoint=>true){1}}.should == 1
870
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3',
871
+ 'SAVEPOINT autopoint_1 -- 1', 'SAVEPOINT autopoint_1 -- 2', 'SAVEPOINT autopoint_1 -- 3',
872
+ 'RELEASE SAVEPOINT autopoint_1 -- 3', 'RELEASE SAVEPOINT autopoint_1 -- 2', 'RELEASE SAVEPOINT autopoint_1 -- 1',
873
+ 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
874
+ end
875
+ end
876
+
606
877
  describe "Database#transaction with savepoints" do
607
878
  before do
608
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
609
- @db.meta_def(:supports_savepoints?){true}
879
+ @db = Sequel.mock
610
880
  end
611
881
 
612
882
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
613
883
  @db.transaction {@db.execute 'DROP TABLE test;'}
614
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
884
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
615
885
  end
616
886
 
617
887
  specify "should use savepoints if given the :savepoint option" do
618
888
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}}
619
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
889
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
620
890
  end
621
891
 
622
892
  specify "should not use a savepoints if no transaction is in progress" do
623
893
  @db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
624
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
894
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
625
895
  end
626
896
 
627
897
  specify "should reuse the current transaction if no :savepoint option is given" do
628
898
  @db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
629
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
899
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
630
900
  end
631
901
 
632
902
  specify "should handle returning inside of the block by committing" do
@@ -638,7 +908,7 @@ describe "Database#transaction with savepoints" do
638
908
  end
639
909
  end
640
910
  @db.ret_commit
641
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
911
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
642
912
  end
643
913
 
644
914
  specify "should handle returning inside of a savepoint by committing" do
@@ -652,34 +922,34 @@ describe "Database#transaction with savepoints" do
652
922
  end
653
923
  end
654
924
  @db.ret_commit
655
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
925
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
656
926
  end
657
927
 
658
928
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
659
929
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
660
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
930
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
661
931
 
662
932
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
663
933
  end
664
934
 
665
935
  specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
666
936
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test'; raise RuntimeError}} rescue nil
667
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
937
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
668
938
 
669
939
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
670
940
  end
671
941
 
672
- specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
942
+ specify "should issue ROLLBACK if Sequel::Rollback is raised in the transaction" do
673
943
  @db.transaction do
674
944
  @db.drop_table(:a)
675
945
  raise Sequel::Rollback
676
946
  @db.drop_table(:b)
677
947
  end
678
948
 
679
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
949
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
680
950
  end
681
951
 
682
- specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is called in a savepoint" do
952
+ specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is raised in a savepoint" do
683
953
  @db.transaction do
684
954
  @db.transaction(:savepoint=>true) do
685
955
  @db.drop_table(:a)
@@ -688,7 +958,7 @@ describe "Database#transaction with savepoints" do
688
958
  @db.drop_table(:b)
689
959
  end
690
960
 
691
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
961
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
692
962
  end
693
963
 
694
964
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -704,37 +974,28 @@ end
704
974
 
705
975
  describe "A Database adapter with a scheme" do
706
976
  before do
707
- class CCC < Sequel::Database
708
- if defined?(DISCONNECTS)
709
- DISCONNECTS.clear
710
- else
711
- DISCONNECTS = []
712
- end
713
- set_adapter_scheme :ccc
714
- def disconnect
715
- DISCONNECTS << self
716
- end
717
- end
977
+ @ccc = Class.new(Sequel::Database)
978
+ @ccc.send(:set_adapter_scheme, :ccc)
718
979
  end
719
980
 
720
981
  specify "should be registered in the ADAPTER_MAP" do
721
- Sequel::ADAPTER_MAP[:ccc].should == CCC
982
+ Sequel::ADAPTER_MAP[:ccc].should == @ccc
722
983
  end
723
984
 
724
985
  specify "should give the database_type as the adapter scheme by default" do
725
- CCC.new.database_type.should == :ccc
986
+ @ccc.new.database_type.should == :ccc
726
987
  end
727
988
 
728
989
  specify "should be instantiated when its scheme is specified" do
729
990
  c = Sequel::Database.connect('ccc://localhost/db')
730
- c.should be_a_kind_of(CCC)
991
+ c.should be_a_kind_of(@ccc)
731
992
  c.opts[:host].should == 'localhost'
732
993
  c.opts[:database].should == 'db'
733
994
  end
734
995
 
735
996
  specify "should be accessible through Sequel.connect" do
736
997
  c = Sequel.connect 'ccc://localhost/db'
737
- c.should be_a_kind_of(CCC)
998
+ c.should be_a_kind_of(@ccc)
738
999
  c.opts[:host].should == 'localhost'
739
1000
  c.opts[:database].should == 'db'
740
1001
  end
@@ -746,7 +1007,7 @@ describe "A Database adapter with a scheme" do
746
1007
  returnValue = 'anything'
747
1008
 
748
1009
  p = proc do |c|
749
- c.should be_a_kind_of(CCC)
1010
+ c.should be_a_kind_of(@ccc)
750
1011
  c.opts[:host].should == 'localhost'
751
1012
  c.opts[:database].should == 'db'
752
1013
  z = y
@@ -754,15 +1015,22 @@ describe "A Database adapter with a scheme" do
754
1015
  x = c
755
1016
  returnValue
756
1017
  end
1018
+
1019
+ @ccc.class_eval do
1020
+ self::DISCONNECTS = []
1021
+ def disconnect
1022
+ self.class::DISCONNECTS << self
1023
+ end
1024
+ end
757
1025
  Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
758
- CCC::DISCONNECTS.should == [x]
1026
+ @ccc::DISCONNECTS.should == [x]
759
1027
 
760
1028
  Sequel.connect('ccc://localhost/db', &p).should == returnValue
761
- CCC::DISCONNECTS.should == [y, x]
1029
+ @ccc::DISCONNECTS.should == [y, x]
762
1030
 
763
1031
  Sequel.send(:def_adapter_method, :ccc)
764
1032
  Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
765
- CCC::DISCONNECTS.should == [z, y, x]
1033
+ @ccc::DISCONNECTS.should == [z, y, x]
766
1034
  end
767
1035
 
768
1036
  specify "should be accessible through Sequel.<adapter>" do
@@ -773,31 +1041,31 @@ describe "A Database adapter with a scheme" do
773
1041
 
774
1042
  c = Sequel.ccc('mydb')
775
1043
  p = proc{c.opts.delete_if{|k,v| k == :disconnection_proc || k == :single_threaded}}
776
- c.should be_a_kind_of(CCC)
777
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>CCC}
1044
+ c.should be_a_kind_of(@ccc)
1045
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>@ccc}
778
1046
 
779
1047
  c = Sequel.ccc('mydb', :host => 'localhost')
780
- c.should be_a_kind_of(CCC)
781
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1048
+ c.should be_a_kind_of(@ccc)
1049
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
782
1050
 
783
1051
  c = Sequel.ccc
784
- c.should be_a_kind_of(CCC)
785
- p.call.should == {:adapter=>:ccc, :adapter_class=>CCC}
1052
+ c.should be_a_kind_of(@ccc)
1053
+ p.call.should == {:adapter=>:ccc, :adapter_class=>@ccc}
786
1054
 
787
1055
  c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
788
- c.should be_a_kind_of(CCC)
789
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1056
+ c.should be_a_kind_of(@ccc)
1057
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
790
1058
  end
791
1059
 
792
1060
  specify "should be accessible through Sequel.connect with options" do
793
1061
  c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
794
- c.should be_a_kind_of(CCC)
1062
+ c.should be_a_kind_of(@ccc)
795
1063
  c.opts[:adapter].should == :ccc
796
1064
  end
797
1065
 
798
1066
  specify "should be accessible through Sequel.connect with URL parameters" do
799
1067
  c = Sequel.connect 'ccc:///db?host=/tmp&user=test'
800
- c.should be_a_kind_of(CCC)
1068
+ c.should be_a_kind_of(@ccc)
801
1069
  c.opts[:host].should == '/tmp'
802
1070
  c.opts[:database].should == 'db'
803
1071
  c.opts[:user].should == 'test'
@@ -805,14 +1073,14 @@ describe "A Database adapter with a scheme" do
805
1073
 
806
1074
  specify "should have URL parameters take precedence over fixed URL parts" do
807
1075
  c = Sequel.connect 'ccc://localhost/db?host=a&database=b'
808
- c.should be_a_kind_of(CCC)
1076
+ c.should be_a_kind_of(@ccc)
809
1077
  c.opts[:host].should == 'a'
810
1078
  c.opts[:database].should == 'b'
811
1079
  end
812
1080
 
813
1081
  specify "should have hash options take predence over URL parameters or parts" do
814
1082
  c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=>'a', :database=>'b', :user=>'c'
815
- c.should be_a_kind_of(CCC)
1083
+ c.should be_a_kind_of(@ccc)
816
1084
  c.opts[:host].should == 'a'
817
1085
  c.opts[:database].should == 'b'
818
1086
  c.opts[:user].should == 'c'
@@ -820,7 +1088,7 @@ describe "A Database adapter with a scheme" do
820
1088
 
821
1089
  specify "should unescape values of URL parameters and parts" do
822
1090
  c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
823
- c.should be_a_kind_of(CCC)
1091
+ c.should be_a_kind_of(@ccc)
824
1092
  c.opts[:database].should == 'd[b]'
825
1093
  c.opts[:host].should == 'domain\\instance'
826
1094
  end
@@ -935,10 +1203,6 @@ describe "A single threaded database" do
935
1203
  end
936
1204
 
937
1205
  describe "A database" do
938
- before do
939
- Sequel::Database.single_threaded = false
940
- end
941
-
942
1206
  after do
943
1207
  Sequel::Database.single_threaded = false
944
1208
  end
@@ -983,11 +1247,7 @@ end
983
1247
 
984
1248
  describe "Database#fetch" do
985
1249
  before do
986
- @db = Sequel::Database.new
987
- c = Class.new(Sequel::Dataset) do
988
- def fetch_rows(sql); yield({:sql => sql}); end
989
- end
990
- @db.meta_def(:dataset) {c.new(self)}
1250
+ @db = Sequel.mock(:fetch=>proc{|sql| {:sql => sql}})
991
1251
  end
992
1252
 
993
1253
  specify "should create a dataset and invoke its fetch_rows method with the given sql" do
@@ -1034,21 +1294,17 @@ end
1034
1294
 
1035
1295
  describe "Database#[]" do
1036
1296
  before do
1037
- @db = Sequel::Database.new
1297
+ @db = Sequel.mock
1038
1298
  end
1039
1299
 
1040
1300
  specify "should return a dataset when symbols are given" do
1041
1301
  ds = @db[:items]
1042
- ds.class.should == Sequel::Dataset
1302
+ ds.should be_a_kind_of(Sequel::Dataset)
1043
1303
  ds.opts[:from].should == [:items]
1044
1304
  end
1045
1305
 
1046
1306
  specify "should return a dataset when a string is given" do
1047
- c = Class.new(Sequel::Dataset) do
1048
- def fetch_rows(sql); yield({:sql => sql}); end
1049
- end
1050
- @db.meta_def(:dataset) {c.new(self)}
1051
-
1307
+ @db.fetch = proc{|sql| {:sql=>sql}}
1052
1308
  sql = nil
1053
1309
  @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
1054
1310
  sql.should == "select * from xyz where x = 15 and y = 'abc'"
@@ -1056,53 +1312,36 @@ describe "Database#[]" do
1056
1312
  end
1057
1313
 
1058
1314
  describe "Database#inspect" do
1059
- before do
1060
- @db = DummyDatabase.new
1061
-
1062
- @db.meta_def(:uri) {'blah://blahblah/blah'}
1063
- end
1064
-
1065
1315
  specify "should include the class name and the connection url" do
1066
- @db.inspect.should == '#<DummyDatabase: "blah://blahblah/blah">'
1316
+ Sequel.connect('mock://foo/bar').inspect.should == '#<Sequel::Mock::Database: "mock://foo/bar">'
1067
1317
  end
1068
1318
  end
1069
1319
 
1070
1320
  describe "Database#get" do
1071
1321
  before do
1072
- @c = Class.new(DummyDatabase) do
1073
- def dataset
1074
- ds = super
1075
- def ds.get(*args, &block)
1076
- @db.execute select(*args, &block).sql
1077
- args
1078
- end
1079
- ds
1080
- end
1081
- end
1082
-
1083
- @db = @c.new
1322
+ @db = Sequel.mock(:fetch=>{:a=>1})
1084
1323
  end
1085
1324
 
1086
1325
  specify "should use Dataset#get to get a single value" do
1087
- @db.get(1).should == [1]
1088
- @db.sqls.last.should == 'SELECT 1'
1326
+ @db.get(1).should == 1
1327
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1089
1328
 
1090
1329
  @db.get(:version.sql_function)
1091
- @db.sqls.last.should == 'SELECT version()'
1330
+ @db.sqls.should == ['SELECT version() LIMIT 1']
1092
1331
  end
1093
1332
 
1094
1333
  specify "should accept a block" do
1095
1334
  @db.get{1}
1096
- @db.sqls.last.should == 'SELECT 1'
1335
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1097
1336
 
1098
1337
  @db.get{version(1)}
1099
- @db.sqls.last.should == 'SELECT version(1)'
1338
+ @db.sqls.should == ['SELECT version(1) LIMIT 1']
1100
1339
  end
1101
1340
  end
1102
1341
 
1103
1342
  describe "Database#call" do
1104
1343
  specify "should call the prepared statement with the given name" do
1105
- db = MockDatabase.new
1344
+ db = Sequel.mock(:fetch=>{:id => 1, :x => 1})
1106
1345
  db[:items].prepare(:select, :select_all)
1107
1346
  db.call(:select_all).should == [{:id => 1, :x => 1}]
1108
1347
  db[:items].filter(:n=>:$n).prepare(:select, :select_n)
@@ -1114,146 +1353,129 @@ end
1114
1353
  describe "Database#server_opts" do
1115
1354
  specify "should return the general opts if no :servers option is used" do
1116
1355
  opts = {:host=>1, :database=>2}
1117
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1356
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1118
1357
  end
1119
1358
 
1120
1359
  specify "should return the general opts if entry for the server is present in the :servers option" do
1121
1360
  opts = {:host=>1, :database=>2, :servers=>{}}
1122
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1361
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1123
1362
  end
1124
1363
 
1125
1364
  specify "should return the general opts merged with the specific opts if given as a hash" do
1126
1365
  opts = {:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}}}
1127
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 3
1366
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 3
1128
1367
  end
1129
1368
 
1130
1369
  specify "should return the sgeneral opts merged with the specific opts if given as a proc" do
1131
1370
  opts = {:host=>1, :database=>2, :servers=>{:server1=>proc{|db| {:host=>4}}}}
1132
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 4
1371
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 4
1133
1372
  end
1134
1373
 
1135
1374
  specify "should raise an error if the specific opts is not a proc or hash" do
1136
1375
  opts = {:host=>1, :database=>2, :servers=>{:server1=>2}}
1137
- proc{MockDatabase.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1376
+ proc{Sequel::Database.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1138
1377
  end
1139
1378
  end
1140
1379
 
1141
1380
  describe "Database#add_servers" do
1142
1381
  before do
1143
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1144
- def @db.connect(server)
1145
- server_opts(server)
1146
- end
1147
- def @db.disconnect_connection(c)
1148
- end
1382
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1149
1383
  end
1150
1384
 
1151
1385
  specify "should add new servers to the connection pool" do
1152
- @db.synchronize{|c| c[:host].should == 1}
1153
- @db.synchronize(:server1){|c| c[:host].should == 3}
1154
- @db.synchronize(:server2){|c| c[:host].should == 1}
1386
+ @db.synchronize{|c| c.opts[:host].should == 1}
1387
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1388
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1155
1389
 
1156
1390
  @db.add_servers(:server2=>{:host=>6})
1157
- @db.synchronize{|c| c[:host].should == 1}
1158
- @db.synchronize(:server1){|c| c[:host].should == 3}
1159
- @db.synchronize(:server2){|c| c[:host].should == 6}
1391
+ @db.synchronize{|c| c.opts[:host].should == 1}
1392
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1393
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1160
1394
 
1161
1395
  @db.disconnect
1162
- @db.synchronize{|c| c[:host].should == 1}
1163
- @db.synchronize(:server1){|c| c[:host].should == 3}
1164
- @db.synchronize(:server2){|c| c[:host].should == 6}
1396
+ @db.synchronize{|c| c.opts[:host].should == 1}
1397
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1398
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1165
1399
  end
1166
1400
 
1167
1401
  specify "should replace options for future connections to existing servers" do
1168
- @db.synchronize{|c| c[:host].should == 1}
1169
- @db.synchronize(:server1){|c| c[:host].should == 3}
1170
- @db.synchronize(:server2){|c| c[:host].should == 1}
1402
+ @db.synchronize{|c| c.opts[:host].should == 1}
1403
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1404
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1171
1405
 
1172
1406
  @db.add_servers(:default=>proc{{:host=>4}}, :server1=>{:host=>8})
1173
- @db.synchronize{|c| c[:host].should == 1}
1174
- @db.synchronize(:server1){|c| c[:host].should == 3}
1175
- @db.synchronize(:server2){|c| c[:host].should == 1}
1407
+ @db.synchronize{|c| c.opts[:host].should == 1}
1408
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1409
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1176
1410
 
1177
1411
  @db.disconnect
1178
- @db.synchronize{|c| c[:host].should == 4}
1179
- @db.synchronize(:server1){|c| c[:host].should == 8}
1180
- @db.synchronize(:server2){|c| c[:host].should == 4}
1412
+ @db.synchronize{|c| c.opts[:host].should == 4}
1413
+ @db.synchronize(:server1){|c| c.opts[:host].should == 8}
1414
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1181
1415
  end
1182
1416
  end
1183
1417
 
1184
1418
  describe "Database#remove_servers" do
1185
1419
  before do
1186
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1187
- def @db.connect(server)
1188
- server_opts(server)
1189
- end
1190
- def @db.disconnect_connection(c)
1191
- end
1420
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1192
1421
  end
1193
1422
 
1194
1423
  specify "should remove servers from the connection pool" do
1195
- @db.synchronize{|c| c[:host].should == 1}
1196
- @db.synchronize(:server1){|c| c[:host].should == 3}
1197
- @db.synchronize(:server2){|c| c[:host].should == 4}
1424
+ @db.synchronize{|c| c.opts[:host].should == 1}
1425
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1426
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1198
1427
 
1199
1428
  @db.remove_servers(:server1, :server2)
1200
- @db.synchronize{|c| c[:host].should == 1}
1201
- @db.synchronize(:server1){|c| c[:host].should == 1}
1202
- @db.synchronize(:server2){|c| c[:host].should == 1}
1429
+ @db.synchronize{|c| c.opts[:host].should == 1}
1430
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1431
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1203
1432
  end
1204
1433
 
1205
1434
  specify "should accept arrays of symbols" do
1206
1435
  @db.remove_servers([:server1, :server2])
1207
- @db.synchronize{|c| c[:host].should == 1}
1208
- @db.synchronize(:server1){|c| c[:host].should == 1}
1209
- @db.synchronize(:server2){|c| c[:host].should == 1}
1436
+ @db.synchronize{|c| c.opts[:host].should == 1}
1437
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1438
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1210
1439
  end
1211
1440
 
1212
1441
  specify "should allow removal while connections are still open" do
1213
1442
  @db.synchronize do |c1|
1214
- c1[:host].should == 1
1443
+ c1.opts[:host].should == 1
1215
1444
  @db.synchronize(:server1) do |c2|
1216
- c2[:host].should == 3
1445
+ c2.opts[:host].should == 3
1217
1446
  @db.synchronize(:server2) do |c3|
1218
- c3[:host].should == 4
1447
+ c3.opts[:host].should == 4
1219
1448
  @db.remove_servers(:server1, :server2)
1220
1449
  @db.synchronize(:server1) do |c4|
1221
1450
  c4.should_not == c2
1222
1451
  c4.should == c1
1223
- c4[:host].should == 1
1452
+ c4.opts[:host].should == 1
1224
1453
  @db.synchronize(:server2) do |c5|
1225
1454
  c5.should_not == c3
1226
1455
  c5.should == c1
1227
- c5[:host].should == 1
1456
+ c5.opts[:host].should == 1
1228
1457
  end
1229
1458
  end
1230
- c3[:host].should == 4
1459
+ c3.opts[:host].should == 4
1231
1460
  end
1232
- c2[:host].should == 3
1461
+ c2.opts[:host].should == 3
1233
1462
  end
1234
- c1[:host].should == 1
1463
+ c1.opts[:host].should == 1
1235
1464
  end
1236
1465
  end
1237
1466
  end
1238
1467
 
1239
1468
  describe "Database#each_server with do/jdbc adapter connection string without :adapter option" do
1240
- before do
1469
+ specify "should yield a separate database object for each server" do
1241
1470
  klass = Class.new(Sequel::Database)
1242
- klass.should_receive(:adapter_class).once.with(:jdbc).and_return(MockDatabase)
1471
+ klass.should_receive(:adapter_class).once.with(:jdbc).and_return(Sequel::Mock::Database)
1243
1472
  @db = klass.connect('jdbc:blah:', :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1244
- def @db.connect(server)
1245
- server_opts(server)
1246
- end
1247
- def @db.disconnect_connection(c)
1248
- end
1249
- end
1250
1473
 
1251
- specify "should yield a separate database object for each server" do
1252
1474
  hosts = []
1253
1475
  @db.each_server do |db|
1254
1476
  db.should be_a_kind_of(Sequel::Database)
1255
1477
  db.should_not == @db
1256
- db.opts[:adapter_class].should == MockDatabase
1478
+ db.opts[:adapter_class].should == Sequel::Mock::Database
1257
1479
  db.opts[:database].should == 2
1258
1480
  hosts << db.opts[:host]
1259
1481
  end
@@ -1263,12 +1485,7 @@ end
1263
1485
 
1264
1486
  describe "Database#each_server" do
1265
1487
  before do
1266
- @db = Sequel.connect(:adapter=>:mock, :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1267
- def @db.connect(server)
1268
- server_opts(server)
1269
- end
1270
- def @db.disconnect_connection(c)
1271
- end
1488
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1272
1489
  end
1273
1490
 
1274
1491
  specify "should yield a separate database object for each server" do
@@ -1299,21 +1516,25 @@ describe "Database#each_server" do
1299
1516
  end
1300
1517
 
1301
1518
  describe "Database#raise_error" do
1519
+ before do
1520
+ @db = Sequel.mock
1521
+ end
1522
+
1302
1523
  specify "should reraise if the exception class is not in opts[:classes]" do
1303
1524
  e = Class.new(StandardError)
1304
- proc{MockDatabase.new.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1525
+ proc{@db.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1305
1526
  end
1306
1527
 
1307
- specify "should convert the exception to a DatabaseError if the exception class is not in opts[:classes]" do
1308
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1528
+ specify "should convert the exception to a DatabaseError if the exception class is in opts[:classes]" do
1529
+ proc{@db.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1309
1530
  end
1310
1531
 
1311
1532
  specify "should convert the exception to a DatabaseError if opts[:classes] if not present" do
1312
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1533
+ proc{@db.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1313
1534
  end
1314
1535
 
1315
1536
  specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
1316
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1537
+ proc{@db.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1317
1538
  end
1318
1539
  end
1319
1540
 
@@ -1373,6 +1594,49 @@ describe "Database#typecast_value" do
1373
1594
  @db.typecast_value(:date, :year=>Date.today.year, :month=>Date.today.month, :day=>Date.today.day).should == Date.today
1374
1595
  end
1375
1596
 
1597
+ specify "should have Sequel.application_to_database_timestamp convert to Sequel.database_timezone" do
1598
+ begin
1599
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1600
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1601
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1602
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1603
+ Sequel.application_timezone = :utc
1604
+ Sequel.database_timezone = :local
1605
+ Sequel.application_to_database_timestamp(t).should == t4
1606
+ Sequel.application_timezone = :local
1607
+ Sequel.database_timezone = :utc
1608
+ Sequel.application_to_database_timestamp(t2).should == t3
1609
+ ensure
1610
+ Sequel.default_timezone = nil
1611
+ end
1612
+ end
1613
+
1614
+ specify "should have Database#to_application_timestamp convert values using the database's timezone" do
1615
+ begin
1616
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1617
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1618
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1619
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1620
+ Sequel.default_timezone = :utc
1621
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t
1622
+ Sequel.database_timezone = :local
1623
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1624
+ Sequel.default_timezone = :local
1625
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t2
1626
+ Sequel.database_timezone = :utc
1627
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1628
+
1629
+ Sequel.default_timezone = :utc
1630
+ @db.timezone = :local
1631
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1632
+ Sequel.default_timezone = :local
1633
+ @db.timezone = :utc
1634
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1635
+ ensure
1636
+ Sequel.default_timezone = nil
1637
+ end
1638
+ end
1639
+
1376
1640
  specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1377
1641
  t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1378
1642
  t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
@@ -1596,7 +1860,7 @@ describe "Database#blank_object?" do
1596
1860
  end
1597
1861
 
1598
1862
  describe "Database#schema_autoincrementing_primary_key?" do
1599
- specify "should whether the parsed schema row indicates a primary key" do
1863
+ specify "should indicate whether the parsed schema row indicates a primary key" do
1600
1864
  m = Sequel::Database.new.method(:schema_autoincrementing_primary_key?)
1601
1865
  m.call(:primary_key=>true).should == true
1602
1866
  m.call(:primary_key=>false).should == false
@@ -1652,7 +1916,7 @@ describe "Database#column_schema_to_ruby_default" do
1652
1916
  db = Sequel::Database.new
1653
1917
  m = db.method(:column_schema_to_ruby_default)
1654
1918
  p = lambda{|d,t| m.call(d,t)}
1655
- p[nil, :integer].should == nil
1919
+ p[nil, :integer].should be_nil
1656
1920
  p['1', :integer].should == 1
1657
1921
  p['-1', :integer].should == -1
1658
1922
  p['1.0', :float].should == 1.0
@@ -1672,12 +1936,12 @@ describe "Database#column_schema_to_ruby_default" do
1672
1936
  p["'\\a''b'", :string].should == "\\a'b"
1673
1937
  p["'NULL'", :string].should == "NULL"
1674
1938
  p["'2009-10-29'", :date].should == Date.new(2009,10,29)
1675
- p["CURRENT_TIMESTAMP", :date].should == nil
1676
- p["today()", :date].should == nil
1939
+ p["CURRENT_TIMESTAMP", :date].should be_nil
1940
+ p["today()", :date].should be_nil
1677
1941
  p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
1678
1942
  p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1679
1943
  p["'10:20:30'", :time].should == Time.parse('10:20:30')
1680
- p["NaN", :float].should == nil
1944
+ p["NaN", :float].should be_nil
1681
1945
 
1682
1946
  db.meta_def(:database_type){:postgres}
1683
1947
  p["''::text", :string].should == ""
@@ -1701,8 +1965,8 @@ describe "Database#column_schema_to_ruby_default" do
1701
1965
  p["2009-10-29", :date].should == Date.new(2009,10,29)
1702
1966
  p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1703
1967
  p["10:20:30", :time].should == Time.parse('10:20:30')
1704
- p["CURRENT_DATE", :date].should == nil
1705
- p["CURRENT_TIMESTAMP", :datetime].should == nil
1968
+ p["CURRENT_DATE", :date].should be_nil
1969
+ p["CURRENT_TIMESTAMP", :datetime].should be_nil
1706
1970
  p["a", :enum].should == "a"
1707
1971
 
1708
1972
  db.meta_def(:database_type){:mssql}