sequel 3.21.0 → 3.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -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,339 +384,198 @@ describe "Database#dataset" do
384
384
  end
385
385
  end
386
386
 
387
- describe "Database#execute" do
388
- specify "should raise Sequel::NotImplemented" do
389
- 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
- end
392
- end
393
-
394
- describe "Database#tables" do
395
- specify "should raise Sequel::NotImplemented" do
396
- proc {Sequel::Database.new.tables}.should raise_error(Sequel::NotImplemented)
397
- end
398
- end
399
-
400
- describe "Database#indexes" do
401
- specify "should raise Sequel::NotImplemented" do
402
- proc {Sequel::Database.new.indexes(:table)}.should raise_error(Sequel::NotImplemented)
403
- end
404
- end
405
-
406
- describe "Database#<< and run" do
387
+ describe "Database#dataset_class" do
407
388
  before do
408
- sqls = @sqls = []
409
- @c = Class.new(Sequel::Database) do
410
- define_method(:execute_ddl){|sql, *opts| sqls.clear; sqls << sql; sqls.concat(opts)}
411
- end
412
- @db = @c.new({})
413
- end
414
-
415
- specify "should pass the supplied sql to #execute_ddl" do
416
- (@db << "DELETE FROM items")
417
- @sqls.should == ["DELETE FROM items", {}]
418
- @db.run("DELETE FROM items2")
419
- @sqls.should == ["DELETE FROM items2", {}]
420
- end
421
-
422
- specify "should return nil" do
423
- (@db << "DELETE FROM items").should == nil
424
- @db.run("DELETE FROM items").should == nil
389
+ @db = Sequel::Database.new
390
+ @dsc = Class.new(Sequel::Dataset)
425
391
  end
426
392
 
427
- specify "should accept options passed to execute_ddl" do
428
- @db.run("DELETE FROM items", :server=>:s1)
429
- @sqls.should == ["DELETE FROM items", {:server=>:s1}]
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)
430
399
  end
431
- end
432
400
 
433
- describe "Database#synchronize" do
434
- before do
435
- @db = Sequel::Database.new(:max_connections => 1){12345}
436
- end
437
-
438
- specify "should wrap the supplied block in pool.hold" do
439
- stop = false
440
- c1, c2 = nil
441
- t1 = Thread.new {@db.synchronize {|c| c1 = c; while !stop;sleep 0.1;end}}
442
- while !c1;end
443
- c1.should == 12345
444
- t2 = Thread.new {@db.synchronize {|c| c2 = c}}
445
- sleep 0.2
446
- @db.pool.available_connections.should be_empty
447
- c2.should be_nil
448
- stop = true
449
- t1.join
450
- sleep 0.1
451
- c2.should == 12345
452
- t2.join
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
453
405
  end
454
406
  end
455
-
456
- describe "Database#test_connection" do
407
+
408
+ describe "Database#extend_datasets" do
457
409
  before do
458
- @db = Sequel::Database.new{@test = rand(100)}
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)
459
414
  end
460
415
 
461
- specify "should pool#hold" do
462
- @db.test_connection
463
- @test.should_not be_nil
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
464
418
  end
465
-
466
- specify "should return true if successful" do
467
- @db.test_connection.should be_true
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
468
423
  end
469
424
 
470
- specify "should raise an error if the attempting to connect raises an error" do
471
- proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
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)
472
431
  end
473
- end
474
432
 
475
- class DummyDataset < Sequel::Dataset
476
- def first
477
- raise if @opts[:from] == [:a]
478
- true
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]
479
437
  end
480
- end
481
438
 
482
- class DummyDatabase < Sequel::Database
483
- attr_reader :sqls
484
-
485
- def execute(sql, opts={})
486
- @sqls ||= []
487
- @sqls << sql
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]
488
443
  end
489
-
490
- def transaction; yield; end
491
444
 
492
- def dataset
493
- DummyDataset.new(self)
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)
494
447
  end
495
- end
496
448
 
497
- describe "Database#create_table" do
498
- before do
499
- @db = DummyDatabase.new
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'
500
452
  end
501
-
502
- specify "should construct proper SQL" do
503
- @db.create_table :test do
504
- primary_key :id, :integer, :null => false
505
- column :name, :text
506
- index :name, :unique => true
507
- end
508
- @db.sqls.should == [
509
- 'CREATE TABLE test (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
510
- 'CREATE UNIQUE INDEX test_name_index ON test (name)'
511
- ]
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]
512
460
  end
461
+ end
513
462
 
514
- specify "should create a temporary table" do
515
- @db.create_table :test_tmp, :temp => true do
516
- primary_key :id, :integer, :null => false
517
- column :name, :text
518
- index :name, :unique => true
519
- end
520
-
521
- @db.sqls.should == [
522
- 'CREATE TEMPORARY TABLE test_tmp (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, name text)',
523
- 'CREATE UNIQUE INDEX test_tmp_name_index ON test_tmp (name)'
524
- ]
463
+ describe "Database#execute" do
464
+ specify "should raise Sequel::NotImplemented" do
465
+ proc {Sequel::Database.new.execute('blah blah')}.should raise_error(Sequel::NotImplemented)
525
466
  end
526
467
  end
527
468
 
528
- describe "Database#alter_table" do
529
- before do
530
- @db = DummyDatabase.new
531
- end
532
-
533
- specify "should construct proper SQL" do
534
- @db.alter_table :xyz do
535
- add_column :aaa, :text, :null => false, :unique => true
536
- drop_column :bbb
537
- rename_column :ccc, :ddd
538
- set_column_type :eee, :integer
539
- set_column_default :hhh, 'abcd'
540
-
541
- add_index :fff, :unique => true
542
- drop_index :ggg
543
- end
544
-
545
- @db.sqls.should == [
546
- 'ALTER TABLE xyz ADD COLUMN aaa text NOT NULL UNIQUE',
547
- 'ALTER TABLE xyz DROP COLUMN bbb',
548
- 'ALTER TABLE xyz RENAME COLUMN ccc TO ddd',
549
- 'ALTER TABLE xyz ALTER COLUMN eee TYPE integer',
550
- "ALTER TABLE xyz ALTER COLUMN hhh SET DEFAULT 'abcd'",
551
-
552
- 'CREATE UNIQUE INDEX xyz_fff_index ON xyz (fff)',
553
- 'DROP INDEX xyz_ggg_index'
554
- ]
469
+ describe "Database#tables" do
470
+ specify "should raise Sequel::NotImplemented" do
471
+ proc {Sequel::Database.new.tables}.should raise_error(Sequel::NotImplemented)
555
472
  end
556
473
  end
557
474
 
558
- describe "Database#add_column" do
559
- before do
560
- @db = DummyDatabase.new
561
- end
562
-
563
- specify "should construct proper SQL" do
564
- @db.add_column :test, :name, :text, :unique => true
565
- @db.sqls.should == [
566
- 'ALTER TABLE test ADD COLUMN name text UNIQUE'
567
- ]
475
+ describe "Database#views" do
476
+ specify "should raise Sequel::NotImplemented" do
477
+ proc {Sequel::Database.new.views}.should raise_error(Sequel::NotImplemented)
568
478
  end
569
479
  end
570
480
 
571
- describe "Database#drop_column" do
572
- before do
573
- @db = DummyDatabase.new
574
- end
575
-
576
- specify "should construct proper SQL" do
577
- @db.drop_column :test, :name
578
- @db.sqls.should == [
579
- 'ALTER TABLE test DROP COLUMN name'
580
- ]
481
+ describe "Database#indexes" do
482
+ specify "should raise Sequel::NotImplemented" do
483
+ proc {Sequel::Database.new.indexes(:table)}.should raise_error(Sequel::NotImplemented)
581
484
  end
582
485
  end
583
486
 
584
- describe "Database#rename_column" do
487
+ describe "Database#run" do
585
488
  before do
586
- @db = DummyDatabase.new
489
+ @db = Sequel.mock(:servers=>{:s1=>{}})
587
490
  end
588
491
 
589
- specify "should construct proper SQL" do
590
- @db.rename_column :test, :abc, :def
591
- @db.sqls.should == [
592
- 'ALTER TABLE test RENAME COLUMN abc TO def'
593
- ]
594
- end
595
- end
596
-
597
- describe "Database#set_column_type" do
598
- before do
599
- @db = DummyDatabase.new
492
+ specify "should execute the code on the database" do
493
+ @db.run("DELETE FROM items")
494
+ @db.sqls.should == ["DELETE FROM items"]
600
495
  end
601
496
 
602
- specify "should construct proper SQL" do
603
- @db.set_column_type :test, :name, :integer
604
- @db.sqls.should == [
605
- 'ALTER TABLE test ALTER COLUMN name TYPE integer'
606
- ]
607
- end
608
- end
609
-
610
- describe "Database#set_column_default" do
611
- before do
612
- @db = DummyDatabase.new
497
+ specify "should return nil" do
498
+ @db.run("DELETE FROM items").should be_nil
613
499
  end
614
500
 
615
- specify "should construct proper SQL" do
616
- @db.set_column_default :test, :name, 'zyx'
617
- @db.sqls.should == [
618
- "ALTER TABLE test ALTER COLUMN name SET DEFAULT 'zyx'"
619
- ]
501
+ specify "should accept options passed to execute_ddl" do
502
+ @db.run("DELETE FROM items", :server=>:s1)
503
+ @db.sqls.should == ["DELETE FROM items -- s1"]
620
504
  end
621
505
  end
622
506
 
623
- describe "Database#add_index" do
507
+ describe "Database#<<" do
624
508
  before do
625
- @db = DummyDatabase.new
509
+ @db = Sequel.mock
626
510
  end
627
-
628
- specify "should construct proper SQL" do
629
- @db.add_index :test, :name, :unique => true
630
- @db.sqls.should == [
631
- 'CREATE UNIQUE INDEX test_name_index ON test (name)'
632
- ]
511
+
512
+ specify "should execute the code on the database" do
513
+ @db << "DELETE FROM items"
514
+ @db.sqls.should == ["DELETE FROM items"]
633
515
  end
634
516
 
635
- specify "should accept multiple columns" do
636
- @db.add_index :test, [:one, :two]
637
- @db.sqls.should == [
638
- 'CREATE INDEX test_one_two_index ON test (one, two)'
639
- ]
517
+ specify "should be chainable" do
518
+ @db << "DELETE FROM items" << "DELETE FROM items2"
519
+ @db.sqls.should == ["DELETE FROM items", "DELETE FROM items2"]
640
520
  end
641
521
  end
642
522
 
643
- describe "Database#drop_index" do
523
+ describe "Database#synchronize" do
644
524
  before do
645
- @db = DummyDatabase.new
525
+ @db = Sequel::Database.new(:max_connections => 1){12345}
646
526
  end
647
527
 
648
- specify "should construct proper SQL" do
649
- @db.drop_index :test, :name
650
- @db.sqls.should == [
651
- 'DROP INDEX test_name_index'
652
- ]
528
+ specify "should wrap the supplied block in pool.hold" do
529
+ q, q1, q2, q3 = Queue.new, Queue.new, Queue.new, Queue.new
530
+ c1, c2 = nil
531
+ t1 = Thread.new{@db.synchronize{|c| c1 = c; q.push nil; q1.pop}; q.push nil}
532
+ q.pop
533
+ c1.should == 12345
534
+ t2 = Thread.new{@db.synchronize{|c| c2 = c; q2.push nil}}
535
+ @db.pool.available_connections.should be_empty
536
+ c2.should be_nil
537
+ q1.push nil
538
+ q.pop
539
+ q2.pop
540
+ c2.should == 12345
541
+ t1.join
542
+ t2.join
653
543
  end
654
-
655
- end
656
-
657
- class Dummy2Database < Sequel::Database
658
- attr_reader :sql
659
- def execute(sql); @sql = sql; end
660
- def transaction; yield; end
661
544
  end
662
545
 
663
- describe "Database#drop_table" do
546
+ describe "Database#test_connection" do
664
547
  before do
665
- @db = DummyDatabase.new
548
+ @db = Sequel::Database.new{@test = rand(100)}
666
549
  end
667
550
 
668
- specify "should construct proper SQL" do
669
- @db.drop_table :test
670
- @db.sqls.should == ['DROP TABLE test']
551
+ specify "should attempt to get a connection" do
552
+ @db.test_connection
553
+ @test.should_not be_nil
671
554
  end
672
555
 
673
- specify "should accept multiple table names" do
674
- @db.drop_table :a, :bb, :ccc
675
- @db.sqls.should == [
676
- 'DROP TABLE a',
677
- 'DROP TABLE bb',
678
- 'DROP TABLE ccc'
679
- ]
556
+ specify "should return true if successful" do
557
+ @db.test_connection.should be_true
680
558
  end
681
- end
682
559
 
683
- describe "Database#rename_table" do
684
- before do
685
- @db = DummyDatabase.new
686
- end
687
-
688
- specify "should construct proper SQL" do
689
- @db.rename_table :abc, :xyz
690
- @db.sqls.should == ['ALTER TABLE abc RENAME TO xyz']
560
+ specify "should raise an error if the attempting to connect raises an error" do
561
+ proc{Sequel::Database.new{raise Sequel::Error, 'blah'}.test_connection}.should raise_error(Sequel::Error)
691
562
  end
692
563
  end
693
564
 
694
565
  describe "Database#table_exists?" do
695
566
  specify "should try to select the first record from the table's dataset" do
696
- db2 = DummyDatabase.new
697
- db2.table_exists?(:a).should be_false
698
- db2.table_exists?(:b).should be_true
699
- end
700
- end
701
-
702
- class Dummy3Database < Sequel::Database
703
- attr_reader :sql, :transactions
704
- def execute(sql, opts={}); @sql ||= []; @sql << sql; end
705
-
706
- class DummyConnection
707
- def initialize(db); @db = db; end
708
- 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.sqls.should == ["SELECT NULL FROM a LIMIT 1"]
570
+ db.table_exists?(:b).should be_true
571
+ db.table_exists?(:c).should be_true
709
572
  end
710
573
  end
711
574
 
712
- describe "Database#transaction" do
713
- before do
714
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
715
- end
716
-
575
+ shared_examples_for "Database#transaction" do
717
576
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
718
577
  @db.transaction{@db.execute 'DROP TABLE test;'}
719
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
578
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
720
579
  end
721
580
 
722
581
  specify "should support transaction isolation levels" do
@@ -724,7 +583,7 @@ describe "Database#transaction" do
724
583
  [:uncommitted, :committed, :repeatable, :serializable].each do |l|
725
584
  @db.transaction(:isolation=>l){@db.run "DROP TABLE #{l}"}
726
585
  end
727
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
586
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
728
587
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
729
588
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
730
589
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -736,7 +595,7 @@ describe "Database#transaction" do
736
595
  @db.transaction_isolation_level = l
737
596
  @db.transaction{@db.run "DROP TABLE #{l}"}
738
597
  end
739
- @db.sql.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
598
+ @db.sqls.should == ['BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED', 'DROP TABLE uncommitted', 'COMMIT',
740
599
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
741
600
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
742
601
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -751,12 +610,12 @@ describe "Database#transaction" do
751
610
  end
752
611
  end
753
612
  @db.ret_commit
754
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
613
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
755
614
  end
756
615
 
757
616
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
758
617
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
759
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
618
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
760
619
 
761
620
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
762
621
  end
@@ -768,7 +627,50 @@ describe "Database#transaction" do
768
627
  @db.drop_table(:b)
769
628
  end
770
629
 
771
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
630
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
631
+ end
632
+
633
+ specify "should have in_transaction? return true if inside a transaction" do
634
+ c = nil
635
+ @db.transaction{c = @db.in_transaction?}
636
+ c.should be_true
637
+ end
638
+
639
+ specify "should have in_transaction? handle sharding correctly" do
640
+ c = []
641
+ @db.transaction(:server=>:test){c << @db.in_transaction?}
642
+ @db.transaction(:server=>:test){c << @db.in_transaction?(:server=>:test)}
643
+ c.should == [false, true]
644
+ end
645
+
646
+ specify "should have in_transaction? return false if not in a transaction" do
647
+ @db.in_transaction?.should be_false
648
+ end
649
+
650
+ specify "should return nil if Sequel::Rollback is called in the transaction" do
651
+ @db.transaction{raise Sequel::Rollback}.should be_nil
652
+ end
653
+
654
+ specify "should reraise Sequel::Rollback errors when using :rollback=>:reraise option is given" do
655
+ proc {@db.transaction(:rollback=>:reraise){raise Sequel::Rollback}}.should raise_error(Sequel::Rollback)
656
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
657
+ proc {@db.transaction(:rollback=>:reraise){raise ArgumentError}}.should raise_error(ArgumentError)
658
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
659
+ @db.transaction(:rollback=>:reraise){1}.should == 1
660
+ @db.sqls.should == ['BEGIN', 'COMMIT']
661
+ end
662
+
663
+ specify "should always rollback if :rollback=>:always option is given" do
664
+ proc {@db.transaction(:rollback=>:always){raise ArgumentError}}.should raise_error(ArgumentError)
665
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
666
+ @db.transaction(:rollback=>:always){raise Sequel::Rollback}.should be_nil
667
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
668
+ @db.transaction(:rollback=>:always){1}.should be_nil
669
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
670
+ catch (:foo) do
671
+ @db.transaction(:rollback=>:always){throw :foo}
672
+ end
673
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
772
674
  end
773
675
 
774
676
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -780,47 +682,242 @@ describe "Database#transaction" do
780
682
  end
781
683
 
782
684
  specify "should be re-entrant" do
783
- stop = false
685
+ q, q1 = Queue.new, Queue.new
784
686
  cc = nil
785
687
  t = Thread.new do
786
688
  @db.transaction {@db.transaction {@db.transaction {|c|
787
689
  cc = c
788
- while !stop; sleep 0.1; end
690
+ q.pop
691
+ q1.push nil
692
+ q.pop
789
693
  }}}
790
694
  end
791
- while cc.nil?; sleep 0.1; end
792
- cc.should be_a_kind_of(Dummy3Database::DummyConnection)
793
- @db.transactions.should == [t]
794
- stop = true
695
+ q.push nil
696
+ q1.pop
697
+ cc.should be_a_kind_of(Sequel::Mock::Connection)
698
+ tr = @db.instance_variable_get(:@transactions)
699
+ tr.keys.should == [cc]
700
+ q.push nil
795
701
  t.join
796
- @db.transactions.should be_empty
702
+ tr.should be_empty
703
+ end
704
+
705
+ specify "should correctly handle nested transacation use with separate shards" do
706
+ @db.transaction do |c1|
707
+ @db.transaction(:server=>:test) do |c2|
708
+ c1.should_not == c2
709
+ @db.execute 'DROP TABLE test;'
710
+ end
711
+ end
712
+ @db.sqls.should == ['BEGIN', 'BEGIN -- test', 'DROP TABLE test;', 'COMMIT -- test', 'COMMIT']
713
+ end
714
+
715
+ if (!defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
716
+ specify "should handle Thread#kill for transactions inside threads" do
717
+ q = Queue.new
718
+ q1 = Queue.new
719
+ t = Thread.new do
720
+ @db.transaction do
721
+ @db.execute 'DROP TABLE test'
722
+ q1.push nil
723
+ q.pop
724
+ @db.execute 'DROP TABLE test2'
725
+ end
726
+ end
727
+ q1.pop
728
+ t.kill
729
+ t.join
730
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
731
+ end
732
+ end
733
+
734
+ specify "should raise an Error if after_commit or after_rollback is called without a block" do
735
+ proc{@db.after_commit}.should raise_error(Sequel::Error)
736
+ proc{@db.after_rollback}.should raise_error(Sequel::Error)
737
+ end
738
+
739
+ specify "should have after_commit and after_rollback respect :server option" do
740
+ @db.transaction(:server=>:test){@db.after_commit(:server=>:test){@db.execute('foo', :server=>:test)}}
741
+ @db.sqls.should == ['BEGIN -- test', 'COMMIT -- test', 'foo -- test']
742
+ @db.transaction(:server=>:test){@db.after_rollback(:server=>:test){@db.execute('foo', :server=>:test)}; raise Sequel::Rollback}
743
+ @db.sqls.should == ['BEGIN -- test', 'ROLLBACK -- test', 'foo -- test']
744
+ end
745
+
746
+ specify "should execute after_commit outside transactions" do
747
+ @db.after_commit{@db.execute('foo')}
748
+ @db.sqls.should == ['foo']
749
+ end
750
+
751
+ specify "should ignore after_rollback outside transactions" do
752
+ @db.after_rollback{@db.execute('foo')}
753
+ @db.sqls.should == []
754
+ end
755
+
756
+ specify "should support after_commit inside transactions" do
757
+ @db.transaction{@db.after_commit{@db.execute('foo')}}
758
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
759
+ end
760
+
761
+ specify "should support after_rollback inside transactions" do
762
+ @db.transaction{@db.after_rollback{@db.execute('foo')}}
763
+ @db.sqls.should == ['BEGIN', 'COMMIT']
764
+ end
765
+
766
+ specify "should not call after_commit if the transaction rolls back" do
767
+ @db.transaction{@db.after_commit{@db.execute('foo')}; raise Sequel::Rollback}
768
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
769
+ end
770
+
771
+ specify "should call after_rollback if the transaction rolls back" do
772
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; raise Sequel::Rollback}
773
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
774
+ end
775
+
776
+ specify "should call multiple after_commit blocks in order if called inside transactions" do
777
+ @db.transaction{@db.after_commit{@db.execute('foo')}; @db.after_commit{@db.execute('bar')}}
778
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo', 'bar']
779
+ end
780
+
781
+ specify "should call multiple after_rollback blocks in order if called inside transactions" do
782
+ @db.transaction{@db.after_rollback{@db.execute('foo')}; @db.after_rollback{@db.execute('bar')}; raise Sequel::Rollback}
783
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo', 'bar']
784
+ end
785
+
786
+ specify "should support after_commit inside nested transactions" do
787
+ @db.transaction{@db.transaction{@db.after_commit{@db.execute('foo')}}}
788
+ @db.sqls.should == ['BEGIN', 'COMMIT', 'foo']
789
+ end
790
+
791
+ specify "should support after_rollback inside nested transactions" do
792
+ @db.transaction{@db.transaction{@db.after_rollback{@db.execute('foo')}}; raise Sequel::Rollback}
793
+ @db.sqls.should == ['BEGIN', 'ROLLBACK', 'foo']
794
+ end
795
+
796
+ specify "should raise an error if you attempt to use after_commit inside a prepared transaction" do
797
+ @db.meta_def(:supports_prepared_transactions?){true}
798
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_commit{@db.execute('foo')}}}.should raise_error(Sequel::Error)
799
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
800
+ end
801
+
802
+ specify "should raise an error if you attempt to use after_rollback inside a prepared transaction" do
803
+ @db.meta_def(:supports_prepared_transactions?){true}
804
+ proc{@db.transaction(:prepare=>'XYZ'){@db.after_rollback{@db.execute('foo')}}}.should raise_error(Sequel::Error)
805
+ @db.sqls.should == ['BEGIN', 'ROLLBACK']
797
806
  end
798
807
  end
799
808
 
800
- describe "Database#transaction with savepoints" do
809
+ describe "Database#transaction with savepoint support" do
801
810
  before do
802
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
811
+ @db = Sequel.mock(:servers=>{:test=>{}})
812
+ end
813
+
814
+ it_should_behave_like "Database#transaction"
815
+
816
+ specify "should support after_commit inside savepoints" do
803
817
  @db.meta_def(:supports_savepoints?){true}
818
+ @db.transaction do
819
+ @db.after_commit{@db.execute('foo')}
820
+ @db.transaction(:savepoint=>true){@db.after_commit{@db.execute('bar')}}
821
+ @db.after_commit{@db.execute('baz')}
822
+ end
823
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT', 'foo', 'bar', 'baz']
824
+ end
825
+
826
+ specify "should support after_rollback inside savepoints" do
827
+ @db.meta_def(:supports_savepoints?){true}
828
+ @db.transaction do
829
+ @db.after_rollback{@db.execute('foo')}
830
+ @db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('bar')}}
831
+ @db.after_rollback{@db.execute('baz')}
832
+ raise Sequel::Rollback
833
+ end
834
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'RELEASE SAVEPOINT autopoint_1', 'ROLLBACK', 'foo', 'bar', 'baz']
835
+ end
836
+
837
+ specify "should raise an error if you attempt to use after_commit inside a savepoint in a prepared transaction" do
838
+ @db.meta_def(:supports_savepoints?){true}
839
+ @db.meta_def(:supports_prepared_transactions?){true}
840
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_commit{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
841
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
842
+ end
843
+
844
+ specify "should raise an error if you attempt to use after_rollback inside a savepoint in a prepared transaction" do
845
+ @db.meta_def(:supports_savepoints?){true}
846
+ @db.meta_def(:supports_prepared_transactions?){true}
847
+ proc{@db.transaction(:prepare=>'XYZ'){@db.transaction(:savepoint=>true){@db.after_rollback{@db.execute('foo')}}}}.should raise_error(Sequel::Error)
848
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1','ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
849
+ end
850
+ end
851
+
852
+ describe "Database#transaction without savepoint support" do
853
+ before do
854
+ @db = Sequel.mock(:servers=>{:test=>{}})
855
+ @db.meta_def(:supports_savepoints?){false}
856
+ end
857
+
858
+ it_should_behave_like "Database#transaction"
859
+ end
860
+
861
+ describe "Sequel.transaction" do
862
+ before do
863
+ @sqls = []
864
+ @db1 = Sequel.mock(:append=>'1', :sqls=>@sqls)
865
+ @db2 = Sequel.mock(:append=>'2', :sqls=>@sqls)
866
+ @db3 = Sequel.mock(:append=>'3', :sqls=>@sqls)
867
+ end
868
+
869
+ specify "should run the block inside transacitons on all three databases" do
870
+ Sequel.transaction([@db1, @db2, @db3]){1}.should == 1
871
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
872
+ end
873
+
874
+ specify "should pass options to all the blocks" do
875
+ Sequel.transaction([@db1, @db2, @db3], :rollback=>:always){1}.should be_nil
876
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
877
+ end
878
+
879
+ specify "should handle Sequel::Rollback exceptions raised by the block to rollback on all databases" do
880
+ Sequel.transaction([@db1, @db2, @db3]){raise Sequel::Rollback}.should be_nil
881
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'ROLLBACK -- 3', 'ROLLBACK -- 2', 'ROLLBACK -- 1']
882
+ end
883
+
884
+ specify "should handle nested transactions" do
885
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3]){1}}.should == 1
886
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3', 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
887
+ end
888
+
889
+ specify "should handle savepoints" do
890
+ Sequel.transaction([@db1, @db2, @db3]){Sequel.transaction([@db1, @db2, @db3], :savepoint=>true){1}}.should == 1
891
+ @sqls.should == ['BEGIN -- 1', 'BEGIN -- 2', 'BEGIN -- 3',
892
+ 'SAVEPOINT autopoint_1 -- 1', 'SAVEPOINT autopoint_1 -- 2', 'SAVEPOINT autopoint_1 -- 3',
893
+ 'RELEASE SAVEPOINT autopoint_1 -- 3', 'RELEASE SAVEPOINT autopoint_1 -- 2', 'RELEASE SAVEPOINT autopoint_1 -- 1',
894
+ 'COMMIT -- 3', 'COMMIT -- 2', 'COMMIT -- 1']
895
+ end
896
+ end
897
+
898
+ describe "Database#transaction with savepoints" do
899
+ before do
900
+ @db = Sequel.mock
804
901
  end
805
902
 
806
903
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
807
904
  @db.transaction {@db.execute 'DROP TABLE test;'}
808
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
905
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
809
906
  end
810
907
 
811
908
  specify "should use savepoints if given the :savepoint option" do
812
909
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}}
813
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
910
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
814
911
  end
815
912
 
816
913
  specify "should not use a savepoints if no transaction is in progress" do
817
914
  @db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
818
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
915
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
819
916
  end
820
917
 
821
918
  specify "should reuse the current transaction if no :savepoint option is given" do
822
919
  @db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
823
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
920
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
824
921
  end
825
922
 
826
923
  specify "should handle returning inside of the block by committing" do
@@ -832,7 +929,7 @@ describe "Database#transaction with savepoints" do
832
929
  end
833
930
  end
834
931
  @db.ret_commit
835
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
932
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
836
933
  end
837
934
 
838
935
  specify "should handle returning inside of a savepoint by committing" do
@@ -846,34 +943,34 @@ describe "Database#transaction with savepoints" do
846
943
  end
847
944
  end
848
945
  @db.ret_commit
849
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
946
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
850
947
  end
851
948
 
852
949
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
853
950
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
854
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
951
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
855
952
 
856
953
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
857
954
  end
858
955
 
859
956
  specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
860
957
  @db.transaction{@db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test'; raise RuntimeError}} rescue nil
861
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
958
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
862
959
 
863
960
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
864
961
  end
865
962
 
866
- specify "should issue ROLLBACK if Sequel::Rollback is called in the transaction" do
963
+ specify "should issue ROLLBACK if Sequel::Rollback is raised in the transaction" do
867
964
  @db.transaction do
868
965
  @db.drop_table(:a)
869
966
  raise Sequel::Rollback
870
967
  @db.drop_table(:b)
871
968
  end
872
969
 
873
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
970
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
874
971
  end
875
972
 
876
- specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is called in a savepoint" do
973
+ specify "should issue ROLLBACK SAVEPOINT if Sequel::Rollback is raised in a savepoint" do
877
974
  @db.transaction do
878
975
  @db.transaction(:savepoint=>true) do
879
976
  @db.drop_table(:a)
@@ -882,7 +979,7 @@ describe "Database#transaction with savepoints" do
882
979
  @db.drop_table(:b)
883
980
  end
884
981
 
885
- @db.sql.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
982
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE a', 'ROLLBACK TO SAVEPOINT autopoint_1', 'DROP TABLE b', 'COMMIT']
886
983
  end
887
984
 
888
985
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -898,37 +995,28 @@ end
898
995
 
899
996
  describe "A Database adapter with a scheme" do
900
997
  before do
901
- class CCC < Sequel::Database
902
- if defined?(DISCONNECTS)
903
- DISCONNECTS.clear
904
- else
905
- DISCONNECTS = []
906
- end
907
- set_adapter_scheme :ccc
908
- def disconnect
909
- DISCONNECTS << self
910
- end
911
- end
998
+ @ccc = Class.new(Sequel::Database)
999
+ @ccc.send(:set_adapter_scheme, :ccc)
912
1000
  end
913
1001
 
914
1002
  specify "should be registered in the ADAPTER_MAP" do
915
- Sequel::ADAPTER_MAP[:ccc].should == CCC
1003
+ Sequel::ADAPTER_MAP[:ccc].should == @ccc
916
1004
  end
917
1005
 
918
1006
  specify "should give the database_type as the adapter scheme by default" do
919
- CCC.new.database_type.should == :ccc
1007
+ @ccc.new.database_type.should == :ccc
920
1008
  end
921
1009
 
922
1010
  specify "should be instantiated when its scheme is specified" do
923
1011
  c = Sequel::Database.connect('ccc://localhost/db')
924
- c.should be_a_kind_of(CCC)
1012
+ c.should be_a_kind_of(@ccc)
925
1013
  c.opts[:host].should == 'localhost'
926
1014
  c.opts[:database].should == 'db'
927
1015
  end
928
1016
 
929
1017
  specify "should be accessible through Sequel.connect" do
930
1018
  c = Sequel.connect 'ccc://localhost/db'
931
- c.should be_a_kind_of(CCC)
1019
+ c.should be_a_kind_of(@ccc)
932
1020
  c.opts[:host].should == 'localhost'
933
1021
  c.opts[:database].should == 'db'
934
1022
  end
@@ -940,7 +1028,7 @@ describe "A Database adapter with a scheme" do
940
1028
  returnValue = 'anything'
941
1029
 
942
1030
  p = proc do |c|
943
- c.should be_a_kind_of(CCC)
1031
+ c.should be_a_kind_of(@ccc)
944
1032
  c.opts[:host].should == 'localhost'
945
1033
  c.opts[:database].should == 'db'
946
1034
  z = y
@@ -948,15 +1036,22 @@ describe "A Database adapter with a scheme" do
948
1036
  x = c
949
1037
  returnValue
950
1038
  end
1039
+
1040
+ @ccc.class_eval do
1041
+ self::DISCONNECTS = []
1042
+ def disconnect
1043
+ self.class::DISCONNECTS << self
1044
+ end
1045
+ end
951
1046
  Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
952
- CCC::DISCONNECTS.should == [x]
1047
+ @ccc::DISCONNECTS.should == [x]
953
1048
 
954
1049
  Sequel.connect('ccc://localhost/db', &p).should == returnValue
955
- CCC::DISCONNECTS.should == [y, x]
1050
+ @ccc::DISCONNECTS.should == [y, x]
956
1051
 
957
1052
  Sequel.send(:def_adapter_method, :ccc)
958
1053
  Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
959
- CCC::DISCONNECTS.should == [z, y, x]
1054
+ @ccc::DISCONNECTS.should == [z, y, x]
960
1055
  end
961
1056
 
962
1057
  specify "should be accessible through Sequel.<adapter>" do
@@ -967,31 +1062,31 @@ describe "A Database adapter with a scheme" do
967
1062
 
968
1063
  c = Sequel.ccc('mydb')
969
1064
  p = proc{c.opts.delete_if{|k,v| k == :disconnection_proc || k == :single_threaded}}
970
- c.should be_a_kind_of(CCC)
971
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>CCC}
1065
+ c.should be_a_kind_of(@ccc)
1066
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>@ccc}
972
1067
 
973
1068
  c = Sequel.ccc('mydb', :host => 'localhost')
974
- c.should be_a_kind_of(CCC)
975
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1069
+ c.should be_a_kind_of(@ccc)
1070
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
976
1071
 
977
1072
  c = Sequel.ccc
978
- c.should be_a_kind_of(CCC)
979
- p.call.should == {:adapter=>:ccc, :adapter_class=>CCC}
1073
+ c.should be_a_kind_of(@ccc)
1074
+ p.call.should == {:adapter=>:ccc, :adapter_class=>@ccc}
980
1075
 
981
1076
  c = Sequel.ccc(:database => 'mydb', :host => 'localhost')
982
- c.should be_a_kind_of(CCC)
983
- p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>CCC}
1077
+ c.should be_a_kind_of(@ccc)
1078
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
984
1079
  end
985
1080
 
986
1081
  specify "should be accessible through Sequel.connect with options" do
987
1082
  c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
988
- c.should be_a_kind_of(CCC)
1083
+ c.should be_a_kind_of(@ccc)
989
1084
  c.opts[:adapter].should == :ccc
990
1085
  end
991
1086
 
992
1087
  specify "should be accessible through Sequel.connect with URL parameters" do
993
1088
  c = Sequel.connect 'ccc:///db?host=/tmp&user=test'
994
- c.should be_a_kind_of(CCC)
1089
+ c.should be_a_kind_of(@ccc)
995
1090
  c.opts[:host].should == '/tmp'
996
1091
  c.opts[:database].should == 'db'
997
1092
  c.opts[:user].should == 'test'
@@ -999,14 +1094,14 @@ describe "A Database adapter with a scheme" do
999
1094
 
1000
1095
  specify "should have URL parameters take precedence over fixed URL parts" do
1001
1096
  c = Sequel.connect 'ccc://localhost/db?host=a&database=b'
1002
- c.should be_a_kind_of(CCC)
1097
+ c.should be_a_kind_of(@ccc)
1003
1098
  c.opts[:host].should == 'a'
1004
1099
  c.opts[:database].should == 'b'
1005
1100
  end
1006
1101
 
1007
1102
  specify "should have hash options take predence over URL parameters or parts" do
1008
1103
  c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=>'a', :database=>'b', :user=>'c'
1009
- c.should be_a_kind_of(CCC)
1104
+ c.should be_a_kind_of(@ccc)
1010
1105
  c.opts[:host].should == 'a'
1011
1106
  c.opts[:database].should == 'b'
1012
1107
  c.opts[:user].should == 'c'
@@ -1014,7 +1109,7 @@ describe "A Database adapter with a scheme" do
1014
1109
 
1015
1110
  specify "should unescape values of URL parameters and parts" do
1016
1111
  c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
1017
- c.should be_a_kind_of(CCC)
1112
+ c.should be_a_kind_of(@ccc)
1018
1113
  c.opts[:database].should == 'd[b]'
1019
1114
  c.opts[:host].should == 'domain\\instance'
1020
1115
  end
@@ -1129,10 +1224,6 @@ describe "A single threaded database" do
1129
1224
  end
1130
1225
 
1131
1226
  describe "A database" do
1132
- before do
1133
- Sequel::Database.single_threaded = false
1134
- end
1135
-
1136
1227
  after do
1137
1228
  Sequel::Database.single_threaded = false
1138
1229
  end
@@ -1177,11 +1268,7 @@ end
1177
1268
 
1178
1269
  describe "Database#fetch" do
1179
1270
  before do
1180
- @db = Sequel::Database.new
1181
- c = Class.new(Sequel::Dataset) do
1182
- def fetch_rows(sql); yield({:sql => sql}); end
1183
- end
1184
- @db.meta_def(:dataset) {c.new(self)}
1271
+ @db = Sequel.mock(:fetch=>proc{|sql| {:sql => sql}})
1185
1272
  end
1186
1273
 
1187
1274
  specify "should create a dataset and invoke its fetch_rows method with the given sql" do
@@ -1228,143 +1315,54 @@ end
1228
1315
 
1229
1316
  describe "Database#[]" do
1230
1317
  before do
1231
- @db = Sequel::Database.new
1318
+ @db = Sequel.mock
1232
1319
  end
1233
1320
 
1234
1321
  specify "should return a dataset when symbols are given" do
1235
1322
  ds = @db[:items]
1236
- ds.class.should == Sequel::Dataset
1323
+ ds.should be_a_kind_of(Sequel::Dataset)
1237
1324
  ds.opts[:from].should == [:items]
1238
1325
  end
1239
1326
 
1240
1327
  specify "should return a dataset when a string is given" do
1241
- c = Class.new(Sequel::Dataset) do
1242
- def fetch_rows(sql); yield({:sql => sql}); end
1243
- end
1244
- @db.meta_def(:dataset) {c.new(self)}
1245
-
1328
+ @db.fetch = proc{|sql| {:sql=>sql}}
1246
1329
  sql = nil
1247
1330
  @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
1248
1331
  sql.should == "select * from xyz where x = 15 and y = 'abc'"
1249
1332
  end
1250
1333
  end
1251
1334
 
1252
- describe "Database#create_view" do
1253
- before do
1254
- @db = DummyDatabase.new
1255
- end
1256
-
1257
- specify "should construct proper SQL with raw SQL" do
1258
- @db.create_view :test, "SELECT * FROM xyz"
1259
- @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1260
- @db.sqls.clear
1261
- @db.create_view :test.identifier, "SELECT * FROM xyz"
1262
- @db.sqls.should == ['CREATE VIEW test AS SELECT * FROM xyz']
1263
- end
1264
-
1265
- specify "should construct proper SQL with dataset" do
1266
- @db.create_view :test, @db[:items].select(:a, :b).order(:c)
1267
- @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1268
- @db.sqls.clear
1269
- @db.create_view :test.qualify(:sch), @db[:items].select(:a, :b).order(:c)
1270
- @db.sqls.should == ['CREATE VIEW sch.test AS SELECT a, b FROM items ORDER BY c']
1271
- end
1272
- end
1273
-
1274
- describe "Database#create_or_replace_view" do
1275
- before do
1276
- @db = DummyDatabase.new
1277
- end
1278
-
1279
- specify "should construct proper SQL with raw SQL" do
1280
- @db.create_or_replace_view :test, "SELECT * FROM xyz"
1281
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT * FROM xyz']
1282
- @db.sqls.clear
1283
- @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1284
- @db.sqls.should == ['CREATE OR REPLACE VIEW sch.test AS SELECT * FROM xyz']
1285
- end
1286
-
1287
- specify "should construct proper SQL with dataset" do
1288
- @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1289
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1290
- @db.sqls.clear
1291
- @db.create_or_replace_view :test.identifier, @db[:items].select(:a, :b).order(:c)
1292
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1293
- end
1294
- end
1295
-
1296
- describe "Database#drop_view" do
1297
- before do
1298
- @db = DummyDatabase.new
1299
- end
1300
-
1301
- specify "should construct proper SQL" do
1302
- @db.drop_view :test
1303
- @db.drop_view :test.identifier
1304
- @db.drop_view :sch__test
1305
- @db.drop_view :test.qualify(:sch)
1306
- @db.sqls.should == ['DROP VIEW test', 'DROP VIEW test', 'DROP VIEW sch.test', 'DROP VIEW sch.test']
1307
- end
1308
- end
1309
-
1310
- describe "Database#alter_table_sql" do
1311
- before do
1312
- @db = DummyDatabase.new
1313
- end
1314
-
1315
- specify "should raise error for an invalid op" do
1316
- proc {@db.send(:alter_table_sql, :mau, :op => :blah)}.should raise_error(Sequel::Error)
1317
- end
1318
- end
1319
-
1320
1335
  describe "Database#inspect" do
1321
- before do
1322
- @db = DummyDatabase.new
1323
-
1324
- @db.meta_def(:uri) {'blah://blahblah/blah'}
1325
- end
1326
-
1327
1336
  specify "should include the class name and the connection url" do
1328
- @db.inspect.should == '#<DummyDatabase: "blah://blahblah/blah">'
1337
+ Sequel.connect('mock://foo/bar').inspect.should == '#<Sequel::Mock::Database: "mock://foo/bar">'
1329
1338
  end
1330
1339
  end
1331
1340
 
1332
1341
  describe "Database#get" do
1333
1342
  before do
1334
- @c = Class.new(DummyDatabase) do
1335
- def dataset
1336
- ds = super
1337
- def ds.get(*args, &block)
1338
- @db.execute select(*args, &block).sql
1339
- args
1340
- end
1341
- ds
1342
- end
1343
- end
1344
-
1345
- @db = @c.new
1343
+ @db = Sequel.mock(:fetch=>{:a=>1})
1346
1344
  end
1347
1345
 
1348
1346
  specify "should use Dataset#get to get a single value" do
1349
- @db.get(1).should == [1]
1350
- @db.sqls.last.should == 'SELECT 1'
1347
+ @db.get(1).should == 1
1348
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1351
1349
 
1352
1350
  @db.get(:version.sql_function)
1353
- @db.sqls.last.should == 'SELECT version()'
1351
+ @db.sqls.should == ['SELECT version() LIMIT 1']
1354
1352
  end
1355
1353
 
1356
1354
  specify "should accept a block" do
1357
1355
  @db.get{1}
1358
- @db.sqls.last.should == 'SELECT 1'
1356
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1359
1357
 
1360
1358
  @db.get{version(1)}
1361
- @db.sqls.last.should == 'SELECT version(1)'
1359
+ @db.sqls.should == ['SELECT version(1) LIMIT 1']
1362
1360
  end
1363
1361
  end
1364
1362
 
1365
1363
  describe "Database#call" do
1366
1364
  specify "should call the prepared statement with the given name" do
1367
- db = MockDatabase.new
1365
+ db = Sequel.mock(:fetch=>{:id => 1, :x => 1})
1368
1366
  db[:items].prepare(:select, :select_all)
1369
1367
  db.call(:select_all).should == [{:id => 1, :x => 1}]
1370
1368
  db[:items].filter(:n=>:$n).prepare(:select, :select_n)
@@ -1376,146 +1374,129 @@ end
1376
1374
  describe "Database#server_opts" do
1377
1375
  specify "should return the general opts if no :servers option is used" do
1378
1376
  opts = {:host=>1, :database=>2}
1379
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1377
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1380
1378
  end
1381
1379
 
1382
1380
  specify "should return the general opts if entry for the server is present in the :servers option" do
1383
1381
  opts = {:host=>1, :database=>2, :servers=>{}}
1384
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1382
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1385
1383
  end
1386
1384
 
1387
1385
  specify "should return the general opts merged with the specific opts if given as a hash" do
1388
1386
  opts = {:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}}}
1389
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 3
1387
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 3
1390
1388
  end
1391
1389
 
1392
1390
  specify "should return the sgeneral opts merged with the specific opts if given as a proc" do
1393
1391
  opts = {:host=>1, :database=>2, :servers=>{:server1=>proc{|db| {:host=>4}}}}
1394
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 4
1392
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 4
1395
1393
  end
1396
1394
 
1397
1395
  specify "should raise an error if the specific opts is not a proc or hash" do
1398
1396
  opts = {:host=>1, :database=>2, :servers=>{:server1=>2}}
1399
- proc{MockDatabase.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1397
+ proc{Sequel::Database.new(opts).send(:server_opts, :server1)}.should raise_error(Sequel::Error)
1400
1398
  end
1401
1399
  end
1402
1400
 
1403
1401
  describe "Database#add_servers" do
1404
1402
  before do
1405
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1406
- def @db.connect(server)
1407
- server_opts(server)
1408
- end
1409
- def @db.disconnect_connection(c)
1410
- end
1403
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1411
1404
  end
1412
1405
 
1413
1406
  specify "should add new servers to the connection pool" do
1414
- @db.synchronize{|c| c[:host].should == 1}
1415
- @db.synchronize(:server1){|c| c[:host].should == 3}
1416
- @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}
1417
1410
 
1418
1411
  @db.add_servers(:server2=>{:host=>6})
1419
- @db.synchronize{|c| c[:host].should == 1}
1420
- @db.synchronize(:server1){|c| c[:host].should == 3}
1421
- @db.synchronize(:server2){|c| c[:host].should == 6}
1412
+ @db.synchronize{|c| c.opts[:host].should == 1}
1413
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1414
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1422
1415
 
1423
1416
  @db.disconnect
1424
- @db.synchronize{|c| c[:host].should == 1}
1425
- @db.synchronize(:server1){|c| c[:host].should == 3}
1426
- @db.synchronize(:server2){|c| c[:host].should == 6}
1417
+ @db.synchronize{|c| c.opts[:host].should == 1}
1418
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1419
+ @db.synchronize(:server2){|c| c.opts[:host].should == 6}
1427
1420
  end
1428
1421
 
1429
1422
  specify "should replace options for future connections to existing servers" do
1430
- @db.synchronize{|c| c[:host].should == 1}
1431
- @db.synchronize(:server1){|c| c[:host].should == 3}
1432
- @db.synchronize(:server2){|c| c[:host].should == 1}
1423
+ @db.synchronize{|c| c.opts[:host].should == 1}
1424
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1425
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1433
1426
 
1434
1427
  @db.add_servers(:default=>proc{{:host=>4}}, :server1=>{:host=>8})
1435
- @db.synchronize{|c| c[:host].should == 1}
1436
- @db.synchronize(:server1){|c| c[:host].should == 3}
1437
- @db.synchronize(:server2){|c| c[:host].should == 1}
1428
+ @db.synchronize{|c| c.opts[:host].should == 1}
1429
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1430
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1438
1431
 
1439
1432
  @db.disconnect
1440
- @db.synchronize{|c| c[:host].should == 4}
1441
- @db.synchronize(:server1){|c| c[:host].should == 8}
1442
- @db.synchronize(:server2){|c| c[:host].should == 4}
1433
+ @db.synchronize{|c| c.opts[:host].should == 4}
1434
+ @db.synchronize(:server1){|c| c.opts[:host].should == 8}
1435
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1443
1436
  end
1444
1437
  end
1445
1438
 
1446
1439
  describe "Database#remove_servers" do
1447
1440
  before do
1448
- @db = MockDatabase.new(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1449
- def @db.connect(server)
1450
- server_opts(server)
1451
- end
1452
- def @db.disconnect_connection(c)
1453
- end
1441
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1454
1442
  end
1455
1443
 
1456
1444
  specify "should remove servers from the connection pool" do
1457
- @db.synchronize{|c| c[:host].should == 1}
1458
- @db.synchronize(:server1){|c| c[:host].should == 3}
1459
- @db.synchronize(:server2){|c| c[:host].should == 4}
1445
+ @db.synchronize{|c| c.opts[:host].should == 1}
1446
+ @db.synchronize(:server1){|c| c.opts[:host].should == 3}
1447
+ @db.synchronize(:server2){|c| c.opts[:host].should == 4}
1460
1448
 
1461
1449
  @db.remove_servers(:server1, :server2)
1462
- @db.synchronize{|c| c[:host].should == 1}
1463
- @db.synchronize(:server1){|c| c[:host].should == 1}
1464
- @db.synchronize(:server2){|c| c[:host].should == 1}
1450
+ @db.synchronize{|c| c.opts[:host].should == 1}
1451
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1452
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1465
1453
  end
1466
1454
 
1467
1455
  specify "should accept arrays of symbols" do
1468
1456
  @db.remove_servers([:server1, :server2])
1469
- @db.synchronize{|c| c[:host].should == 1}
1470
- @db.synchronize(:server1){|c| c[:host].should == 1}
1471
- @db.synchronize(:server2){|c| c[:host].should == 1}
1457
+ @db.synchronize{|c| c.opts[:host].should == 1}
1458
+ @db.synchronize(:server1){|c| c.opts[:host].should == 1}
1459
+ @db.synchronize(:server2){|c| c.opts[:host].should == 1}
1472
1460
  end
1473
1461
 
1474
1462
  specify "should allow removal while connections are still open" do
1475
1463
  @db.synchronize do |c1|
1476
- c1[:host].should == 1
1464
+ c1.opts[:host].should == 1
1477
1465
  @db.synchronize(:server1) do |c2|
1478
- c2[:host].should == 3
1466
+ c2.opts[:host].should == 3
1479
1467
  @db.synchronize(:server2) do |c3|
1480
- c3[:host].should == 4
1468
+ c3.opts[:host].should == 4
1481
1469
  @db.remove_servers(:server1, :server2)
1482
1470
  @db.synchronize(:server1) do |c4|
1483
1471
  c4.should_not == c2
1484
1472
  c4.should == c1
1485
- c4[:host].should == 1
1473
+ c4.opts[:host].should == 1
1486
1474
  @db.synchronize(:server2) do |c5|
1487
1475
  c5.should_not == c3
1488
1476
  c5.should == c1
1489
- c5[:host].should == 1
1477
+ c5.opts[:host].should == 1
1490
1478
  end
1491
1479
  end
1492
- c3[:host].should == 4
1480
+ c3.opts[:host].should == 4
1493
1481
  end
1494
- c2[:host].should == 3
1482
+ c2.opts[:host].should == 3
1495
1483
  end
1496
- c1[:host].should == 1
1484
+ c1.opts[:host].should == 1
1497
1485
  end
1498
1486
  end
1499
1487
  end
1500
1488
 
1501
1489
  describe "Database#each_server with do/jdbc adapter connection string without :adapter option" do
1502
- before do
1490
+ specify "should yield a separate database object for each server" do
1503
1491
  klass = Class.new(Sequel::Database)
1504
- klass.should_receive(:adapter_class).once.with(:jdbc).and_return(MockDatabase)
1492
+ klass.should_receive(:adapter_class).once.with(:jdbc).and_return(Sequel::Mock::Database)
1505
1493
  @db = klass.connect('jdbc:blah:', :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1506
- def @db.connect(server)
1507
- server_opts(server)
1508
- end
1509
- def @db.disconnect_connection(c)
1510
- end
1511
- end
1512
1494
 
1513
- specify "should yield a separate database object for each server" do
1514
1495
  hosts = []
1515
1496
  @db.each_server do |db|
1516
1497
  db.should be_a_kind_of(Sequel::Database)
1517
1498
  db.should_not == @db
1518
- db.opts[:adapter_class].should == MockDatabase
1499
+ db.opts[:adapter_class].should == Sequel::Mock::Database
1519
1500
  db.opts[:database].should == 2
1520
1501
  hosts << db.opts[:host]
1521
1502
  end
@@ -1525,12 +1506,7 @@ end
1525
1506
 
1526
1507
  describe "Database#each_server" do
1527
1508
  before do
1528
- @db = Sequel.connect(:adapter=>:mock, :host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1529
- def @db.connect(server)
1530
- server_opts(server)
1531
- end
1532
- def @db.disconnect_connection(c)
1533
- end
1509
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1534
1510
  end
1535
1511
 
1536
1512
  specify "should yield a separate database object for each server" do
@@ -1561,21 +1537,25 @@ describe "Database#each_server" do
1561
1537
  end
1562
1538
 
1563
1539
  describe "Database#raise_error" do
1540
+ before do
1541
+ @db = Sequel.mock
1542
+ end
1543
+
1564
1544
  specify "should reraise if the exception class is not in opts[:classes]" do
1565
1545
  e = Class.new(StandardError)
1566
- proc{MockDatabase.new.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1546
+ proc{@db.send(:raise_error, e.new(''), :classes=>[])}.should raise_error(e)
1567
1547
  end
1568
1548
 
1569
- specify "should convert the exception to a DatabaseError if the exception class is not in opts[:classes]" do
1570
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1549
+ specify "should convert the exception to a DatabaseError if the exception class is in opts[:classes]" do
1550
+ proc{@db.send(:raise_error, Interrupt.new(''), :classes=>[Interrupt])}.should raise_error(Sequel::DatabaseError)
1571
1551
  end
1572
1552
 
1573
1553
  specify "should convert the exception to a DatabaseError if opts[:classes] if not present" do
1574
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1554
+ proc{@db.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1575
1555
  end
1576
1556
 
1577
1557
  specify "should convert the exception to a DatabaseDisconnectError if opts[:disconnect] is true" do
1578
- proc{MockDatabase.new.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1558
+ proc{@db.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1579
1559
  end
1580
1560
  end
1581
1561
 
@@ -1594,6 +1574,303 @@ describe "Database#typecast_value" do
1594
1574
  proc{@db.typecast_value(:datetime, 4)}.should raise_error(Sequel::InvalidValue)
1595
1575
  end
1596
1576
 
1577
+ specify "should handle integers with leading 0 as base 10" do
1578
+ @db.typecast_value(:integer, "013").should == 13
1579
+ @db.typecast_value(:integer, "08").should == 8
1580
+ @db.typecast_value(:integer, "000013").should == 13
1581
+ @db.typecast_value(:integer, "000008").should == 8
1582
+ end
1583
+
1584
+ specify "should handle integers with leading 0x as base 16" do
1585
+ @db.typecast_value(:integer, "0x013").should == 19
1586
+ @db.typecast_value(:integer, "0x80").should == 128
1587
+ end
1588
+
1589
+ specify "should typecast blobs as as Sequel::SQL::Blob" do
1590
+ v = @db.typecast_value(:blob, "0x013")
1591
+ v.should be_a_kind_of(Sequel::SQL::Blob)
1592
+ v.should == Sequel::SQL::Blob.new("0x013")
1593
+ @db.typecast_value(:blob, v).object_id.should == v.object_id
1594
+ end
1595
+
1596
+ specify "should typecast boolean values to true, false, or nil" do
1597
+ @db.typecast_value(:boolean, false).should be_false
1598
+ @db.typecast_value(:boolean, 0).should be_false
1599
+ @db.typecast_value(:boolean, "0").should be_false
1600
+ @db.typecast_value(:boolean, 'f').should be_false
1601
+ @db.typecast_value(:boolean, 'false').should be_false
1602
+ @db.typecast_value(:boolean, true).should be_true
1603
+ @db.typecast_value(:boolean, 1).should be_true
1604
+ @db.typecast_value(:boolean, '1').should be_true
1605
+ @db.typecast_value(:boolean, 't').should be_true
1606
+ @db.typecast_value(:boolean, 'true').should be_true
1607
+ @db.typecast_value(:boolean, '').should be_nil
1608
+ end
1609
+
1610
+ specify "should typecast date values to Date" do
1611
+ @db.typecast_value(:date, Date.today).should == Date.today
1612
+ @db.typecast_value(:date, DateTime.now).should == Date.today
1613
+ @db.typecast_value(:date, Time.now).should == Date.today
1614
+ @db.typecast_value(:date, Date.today.to_s).should == Date.today
1615
+ @db.typecast_value(:date, :year=>Date.today.year, :month=>Date.today.month, :day=>Date.today.day).should == Date.today
1616
+ end
1617
+
1618
+ specify "should have Sequel.application_to_database_timestamp convert to Sequel.database_timezone" do
1619
+ begin
1620
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1621
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1622
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1623
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1624
+ Sequel.application_timezone = :utc
1625
+ Sequel.database_timezone = :local
1626
+ Sequel.application_to_database_timestamp(t).should == t4
1627
+ Sequel.application_timezone = :local
1628
+ Sequel.database_timezone = :utc
1629
+ Sequel.application_to_database_timestamp(t2).should == t3
1630
+ ensure
1631
+ Sequel.default_timezone = nil
1632
+ end
1633
+ end
1634
+
1635
+ specify "should have Database#to_application_timestamp convert values using the database's timezone" do
1636
+ begin
1637
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1638
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1639
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1640
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1641
+ Sequel.default_timezone = :utc
1642
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t
1643
+ Sequel.database_timezone = :local
1644
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1645
+ Sequel.default_timezone = :local
1646
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t2
1647
+ Sequel.database_timezone = :utc
1648
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1649
+
1650
+ Sequel.default_timezone = :utc
1651
+ @db.timezone = :local
1652
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t3
1653
+ Sequel.default_timezone = :local
1654
+ @db.timezone = :utc
1655
+ @db.to_application_timestamp('2011-01-02 03:04:05').should == t4
1656
+ ensure
1657
+ Sequel.default_timezone = nil
1658
+ end
1659
+ end
1660
+
1661
+ specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1662
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1663
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1664
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1665
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1666
+ dt = DateTime.civil(2011, 1, 2, 3, 4, 5)
1667
+ r1 = defined?(Rational) ? Rational(t2.utc_offset, 86400) : t2.utc_offset/86400.0
1668
+ r2 = defined?(Rational) ? Rational((t - t2).to_i, 86400) : (t - t2).to_i/86400.0
1669
+ dt2 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1)
1670
+ dt3 = DateTime.civil(2011, 1, 2, 3, 4, 5) - r2
1671
+ dt4 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1) + r2
1672
+
1673
+ t.should == t4
1674
+ t2.should == t3
1675
+ dt.should == dt4
1676
+ dt2.should == dt3
1677
+
1678
+ check = proc do |i, o|
1679
+ v = @db.typecast_value(:datetime, i)
1680
+ v.should == o
1681
+ if o.is_a?(Time)
1682
+ v.utc_offset.should == o.utc_offset
1683
+ else
1684
+ v.offset.should == o.offset
1685
+ end
1686
+ end
1687
+ begin
1688
+ @db.typecast_value(:datetime, dt).should == t
1689
+ @db.typecast_value(:datetime, dt2).should == t2
1690
+ @db.typecast_value(:datetime, t).should == t
1691
+ @db.typecast_value(:datetime, t2).should == t2
1692
+ @db.typecast_value(:datetime, dt.to_s).should == t
1693
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == t2
1694
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == Time.mktime(2011, 1, 2, 0, 0, 0)
1695
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == t2
1696
+
1697
+ Sequel.datetime_class = DateTime
1698
+ @db.typecast_value(:datetime, dt).should == dt
1699
+ @db.typecast_value(:datetime, dt2).should == dt2
1700
+ @db.typecast_value(:datetime, t).should == dt
1701
+ @db.typecast_value(:datetime, t2).should == dt2
1702
+ @db.typecast_value(:datetime, dt.to_s).should == dt
1703
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == dt
1704
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == DateTime.civil(2011, 1, 2, 0, 0, 0)
1705
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == dt
1706
+
1707
+ Sequel.application_timezone = :utc
1708
+ Sequel.typecast_timezone = :local
1709
+ Sequel.datetime_class = Time
1710
+ check[dt, t]
1711
+ check[dt2, t3]
1712
+ check[t, t]
1713
+ check[t2, t3]
1714
+ check[dt.to_s, t]
1715
+ check[dt.strftime('%F %T'), t3]
1716
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1717
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t3]
1718
+
1719
+ Sequel.datetime_class = DateTime
1720
+ check[dt, dt]
1721
+ check[dt2, dt3]
1722
+ check[t, dt]
1723
+ check[t2, dt3]
1724
+ check[dt.to_s, dt]
1725
+ check[dt.strftime('%F %T'), dt3]
1726
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1727
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt3]
1728
+
1729
+ Sequel.typecast_timezone = :utc
1730
+ Sequel.datetime_class = Time
1731
+ check[dt, t]
1732
+ check[dt2, t3]
1733
+ check[t, t]
1734
+ check[t2, t3]
1735
+ check[dt.to_s, t]
1736
+ check[dt.strftime('%F %T'), t]
1737
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1738
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t]
1739
+
1740
+ Sequel.datetime_class = DateTime
1741
+ check[dt, dt]
1742
+ check[dt2, dt3]
1743
+ check[t, dt]
1744
+ check[t2, dt3]
1745
+ check[dt.to_s, dt]
1746
+ check[dt.strftime('%F %T'), dt]
1747
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1748
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt]
1749
+
1750
+ Sequel.application_timezone = :local
1751
+ Sequel.datetime_class = Time
1752
+ check[dt, t4]
1753
+ check[dt2, t2]
1754
+ check[t, t4]
1755
+ check[t2, t2]
1756
+ check[dt.to_s, t4]
1757
+ check[dt.strftime('%F %T'), t4]
1758
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1759
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t4]
1760
+
1761
+ Sequel.datetime_class = DateTime
1762
+ check[dt, dt4]
1763
+ check[dt2, dt2]
1764
+ check[t, dt4]
1765
+ check[t2, dt2]
1766
+ check[dt.to_s, dt4]
1767
+ check[dt.strftime('%F %T'), dt4]
1768
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1769
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt4]
1770
+
1771
+ Sequel.typecast_timezone = :local
1772
+ Sequel.datetime_class = Time
1773
+ check[dt, t4]
1774
+ check[dt2, t2]
1775
+ check[t, t4]
1776
+ check[t2, t2]
1777
+ check[dt.to_s, t4]
1778
+ check[dt.strftime('%F %T'), t2]
1779
+ check[Date.civil(2011, 1, 2), Time.local(2011, 1, 2, 0, 0, 0)]
1780
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t2]
1781
+
1782
+ Sequel.datetime_class = DateTime
1783
+ check[dt, dt4]
1784
+ check[dt2, dt2]
1785
+ check[t, dt4]
1786
+ check[t2, dt2]
1787
+ check[dt.to_s, dt4]
1788
+ check[dt.strftime('%F %T'), dt2]
1789
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1790
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt2]
1791
+
1792
+ ensure
1793
+ Sequel.default_timezone = nil
1794
+ Sequel.datetime_class = Time
1795
+ end
1796
+ end
1797
+
1798
+ specify "should handle arrays when typecasting timestamps" do
1799
+ begin
1800
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == Time.local(2011, 10, 11, 12, 13, 14)
1801
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1802
+
1803
+ Sequel.datetime_class = DateTime
1804
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14]).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1805
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1806
+ @db.typecast_value(:datetime, [2011, 10, 11, 12, 13, 14, 500000000, (defined?(Rational) ? Rational(1, 2) : 0.5)]).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1807
+ ensure
1808
+ Sequel.datetime_class = Time
1809
+ end
1810
+ end
1811
+
1812
+ specify "should handle hashes when typecasting timestamps" do
1813
+ begin
1814
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
1815
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1816
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == Time.local(2011, 10, 11, 12, 13, 14)
1817
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == Time.local(2011, 10, 11, 12, 13, 14, 500000)
1818
+
1819
+ Sequel.datetime_class = DateTime
1820
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1821
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1822
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14).should == DateTime.civil(2011, 10, 11, 12, 13, 14)
1823
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5))
1824
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
1825
+ @db.typecast_value(:datetime, :year=>2011, :month=>10, :day=>11, :hour=>12, :minute=>13, :second=>14, :nanos=>500000000, :offset=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1826
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, 14, (defined?(Rational) ? Rational(1, 2) : 0.5))
1827
+ @db.typecast_value(:datetime, 'year'=>2011, 'month'=>10, 'day'=>11, 'hour'=>12, 'minute'=>13, 'second'=>14, 'nanos'=>500000000, 'offset'=>(defined?(Rational) ? Rational(1, 2) : 0.5)).should == DateTime.civil(2011, 10, 11, 12, 13, (defined?(Rational) ? Rational(29, 2) : 14.5), (defined?(Rational) ? Rational(1, 2) : 0.5))
1828
+ ensure
1829
+ Sequel.datetime_class = Time
1830
+ end
1831
+ end
1832
+
1833
+ specify "should typecast decimal values to BigDecimal" do
1834
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1835
+ v = @db.typecast_value(:decimal, i)
1836
+ v.should be_a_kind_of(BigDecimal)
1837
+ v.should == BigDecimal.new('1.0')
1838
+ end
1839
+ end
1840
+
1841
+ specify "should typecast float values to Float" do
1842
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1843
+ v = @db.typecast_value(:float, i)
1844
+ v.should be_a_kind_of(Float)
1845
+ v.should == 1.0
1846
+ end
1847
+ end
1848
+
1849
+ specify "should typecast string values to String" do
1850
+ [1.0, '1.0', '1.0'.to_sequel_blob].each do |i|
1851
+ v = @db.typecast_value(:string, i)
1852
+ v.should be_an_instance_of(String)
1853
+ v.should == "1.0"
1854
+ end
1855
+ end
1856
+
1857
+ specify "should typecast time values to SQLTime" do
1858
+ t = Time.now
1859
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3)
1860
+ [st, Time.utc(t.year, t.month, t.day, 1, 2, 3), Time.local(t.year, t.month, t.day, 1, 2, 3), '01:02:03', {:hour=>1, :minute=>2, :second=>3}].each do |i|
1861
+ v = @db.typecast_value(:time, i)
1862
+ v.should be_an_instance_of(Sequel::SQLTime)
1863
+ v.should == st
1864
+ end
1865
+ end
1866
+
1867
+ specify "should correctly handle time value conversion to SQLTime with fractional seconds" do
1868
+ t = Time.now
1869
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1870
+ t = Time.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1871
+ @db.typecast_value(:time, t).should == st
1872
+ end
1873
+
1597
1874
  specify "should have an underlying exception class available at wrapped_exception" do
1598
1875
  begin
1599
1876
  @db.typecast_value(:date, 'a')
@@ -1639,7 +1916,7 @@ describe "Database#blank_object?" do
1639
1916
  end
1640
1917
 
1641
1918
  describe "Database#schema_autoincrementing_primary_key?" do
1642
- specify "should whether the parsed schema row indicates a primary key" do
1919
+ specify "should indicate whether the parsed schema row indicates a primary key" do
1643
1920
  m = Sequel::Database.new.method(:schema_autoincrementing_primary_key?)
1644
1921
  m.call(:primary_key=>true).should == true
1645
1922
  m.call(:primary_key=>false).should == false
@@ -1695,7 +1972,7 @@ describe "Database#column_schema_to_ruby_default" do
1695
1972
  db = Sequel::Database.new
1696
1973
  m = db.method(:column_schema_to_ruby_default)
1697
1974
  p = lambda{|d,t| m.call(d,t)}
1698
- p[nil, :integer].should == nil
1975
+ p[nil, :integer].should be_nil
1699
1976
  p['1', :integer].should == 1
1700
1977
  p['-1', :integer].should == -1
1701
1978
  p['1.0', :float].should == 1.0
@@ -1715,12 +1992,12 @@ describe "Database#column_schema_to_ruby_default" do
1715
1992
  p["'\\a''b'", :string].should == "\\a'b"
1716
1993
  p["'NULL'", :string].should == "NULL"
1717
1994
  p["'2009-10-29'", :date].should == Date.new(2009,10,29)
1718
- p["CURRENT_TIMESTAMP", :date].should == nil
1719
- p["today()", :date].should == nil
1995
+ p["CURRENT_TIMESTAMP", :date].should be_nil
1996
+ p["today()", :date].should be_nil
1720
1997
  p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
1721
1998
  p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1722
1999
  p["'10:20:30'", :time].should == Time.parse('10:20:30')
1723
- p["NaN", :float].should == nil
2000
+ p["NaN", :float].should be_nil
1724
2001
 
1725
2002
  db.meta_def(:database_type){:postgres}
1726
2003
  p["''::text", :string].should == ""
@@ -1744,8 +2021,8 @@ describe "Database#column_schema_to_ruby_default" do
1744
2021
  p["2009-10-29", :date].should == Date.new(2009,10,29)
1745
2022
  p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1746
2023
  p["10:20:30", :time].should == Time.parse('10:20:30')
1747
- p["CURRENT_DATE", :date].should == nil
1748
- p["CURRENT_TIMESTAMP", :datetime].should == nil
2024
+ p["CURRENT_DATE", :date].should be_nil
2025
+ p["CURRENT_TIMESTAMP", :datetime].should be_nil
1749
2026
  p["a", :enum].should == "a"
1750
2027
 
1751
2028
  db.meta_def(:database_type){:mssql}