sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -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,201 @@ 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.table_exists?(:b).should be_true
570
+ db.table_exists?(:c).should be_true
709
571
  end
710
572
  end
711
573
 
712
574
  describe "Database#transaction" do
713
575
  before do
714
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
576
+ @db = Sequel.mock(:servers=>{:test=>{}})
715
577
  end
716
578
 
717
579
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
718
580
  @db.transaction{@db.execute 'DROP TABLE test;'}
719
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
581
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
720
582
  end
721
583
 
722
584
  specify "should support transaction isolation levels" do
@@ -724,7 +586,7 @@ describe "Database#transaction" do
724
586
  [:uncommitted, :committed, :repeatable, :serializable].each do |l|
725
587
  @db.transaction(:isolation=>l){@db.run "DROP TABLE #{l}"}
726
588
  end
727
- @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',
728
590
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
729
591
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
730
592
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -736,7 +598,7 @@ describe "Database#transaction" do
736
598
  @db.transaction_isolation_level = l
737
599
  @db.transaction{@db.run "DROP TABLE #{l}"}
738
600
  end
739
- @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',
740
602
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL READ COMMITTED', 'DROP TABLE committed', 'COMMIT',
741
603
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ', 'DROP TABLE repeatable', 'COMMIT',
742
604
  'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
@@ -751,12 +613,12 @@ describe "Database#transaction" do
751
613
  end
752
614
  end
753
615
  @db.ret_commit
754
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
616
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
755
617
  end
756
618
 
757
619
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
758
620
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
759
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
621
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
760
622
 
761
623
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
762
624
  end
@@ -768,7 +630,50 @@ describe "Database#transaction" do
768
630
  @db.drop_table(:b)
769
631
  end
770
632
 
771
- @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']
772
677
  end
773
678
 
774
679
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -780,47 +685,218 @@ describe "Database#transaction" do
780
685
  end
781
686
 
782
687
  specify "should be re-entrant" do
783
- stop = false
688
+ q, q1 = Queue.new, Queue.new
784
689
  cc = nil
785
690
  t = Thread.new do
786
691
  @db.transaction {@db.transaction {@db.transaction {|c|
787
692
  cc = c
788
- while !stop; sleep 0.1; end
693
+ q.pop
694
+ q1.push nil
695
+ q.pop
789
696
  }}}
790
697
  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
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
795
704
  t.join
796
- @db.transactions.should be_empty
705
+ tr.should be_empty
706
+ end
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
+
718
+ if (!defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
719
+ specify "should handle Thread#kill for transactions inside threads" do
720
+ q = Queue.new
721
+ q1 = Queue.new
722
+ t = Thread.new do
723
+ @db.transaction do
724
+ @db.execute 'DROP TABLE test'
725
+ q1.push nil
726
+ q.pop
727
+ @db.execute 'DROP TABLE test2'
728
+ end
729
+ end
730
+ q1.pop
731
+ t.kill
732
+ t.join
733
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
734
+ end
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']
797
837
  end
798
838
  end
799
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
+
800
877
  describe "Database#transaction with savepoints" do
801
878
  before do
802
- @db = Dummy3Database.new{Dummy3Database::DummyConnection.new(@db)}
803
- @db.meta_def(:supports_savepoints?){true}
879
+ @db = Sequel.mock
804
880
  end
805
881
 
806
882
  specify "should wrap the supplied block with BEGIN + COMMIT statements" do
807
883
  @db.transaction {@db.execute 'DROP TABLE test;'}
808
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
884
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
809
885
  end
810
886
 
811
887
  specify "should use savepoints if given the :savepoint option" do
812
888
  @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']
889
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test;', 'RELEASE SAVEPOINT autopoint_1', 'COMMIT']
814
890
  end
815
891
 
816
892
  specify "should not use a savepoints if no transaction is in progress" do
817
893
  @db.transaction(:savepoint=>true){@db.execute 'DROP TABLE test;'}
818
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
894
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
819
895
  end
820
896
 
821
897
  specify "should reuse the current transaction if no :savepoint option is given" do
822
898
  @db.transaction{@db.transaction{@db.execute 'DROP TABLE test;'}}
823
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
899
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
824
900
  end
825
901
 
826
902
  specify "should handle returning inside of the block by committing" do
@@ -832,7 +908,7 @@ describe "Database#transaction with savepoints" do
832
908
  end
833
909
  end
834
910
  @db.ret_commit
835
- @db.sql.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
911
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test;', 'COMMIT']
836
912
  end
837
913
 
838
914
  specify "should handle returning inside of a savepoint by committing" do
@@ -846,34 +922,34 @@ describe "Database#transaction with savepoints" do
846
922
  end
847
923
  end
848
924
  @db.ret_commit
849
- @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']
850
926
  end
851
927
 
852
928
  specify "should issue ROLLBACK if an exception is raised, and re-raise" do
853
929
  @db.transaction {@db.execute 'DROP TABLE test'; raise RuntimeError} rescue nil
854
- @db.sql.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
930
+ @db.sqls.should == ['BEGIN', 'DROP TABLE test', 'ROLLBACK']
855
931
 
856
932
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
857
933
  end
858
934
 
859
935
  specify "should issue ROLLBACK SAVEPOINT if an exception is raised inside a savepoint, and re-raise" do
860
936
  @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']
937
+ @db.sqls.should == ['BEGIN', 'SAVEPOINT autopoint_1', 'DROP TABLE test', 'ROLLBACK TO SAVEPOINT autopoint_1', 'ROLLBACK']
862
938
 
863
939
  proc {@db.transaction {raise RuntimeError}}.should raise_error(RuntimeError)
864
940
  end
865
941
 
866
- 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
867
943
  @db.transaction do
868
944
  @db.drop_table(:a)
869
945
  raise Sequel::Rollback
870
946
  @db.drop_table(:b)
871
947
  end
872
948
 
873
- @db.sql.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
949
+ @db.sqls.should == ['BEGIN', 'DROP TABLE a', 'ROLLBACK']
874
950
  end
875
951
 
876
- 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
877
953
  @db.transaction do
878
954
  @db.transaction(:savepoint=>true) do
879
955
  @db.drop_table(:a)
@@ -882,7 +958,7 @@ describe "Database#transaction with savepoints" do
882
958
  @db.drop_table(:b)
883
959
  end
884
960
 
885
- @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']
886
962
  end
887
963
 
888
964
  specify "should raise database errors when commiting a transaction as Sequel::DatabaseError" do
@@ -898,37 +974,28 @@ end
898
974
 
899
975
  describe "A Database adapter with a scheme" do
900
976
  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
977
+ @ccc = Class.new(Sequel::Database)
978
+ @ccc.send(:set_adapter_scheme, :ccc)
912
979
  end
913
980
 
914
981
  specify "should be registered in the ADAPTER_MAP" do
915
- Sequel::ADAPTER_MAP[:ccc].should == CCC
982
+ Sequel::ADAPTER_MAP[:ccc].should == @ccc
916
983
  end
917
984
 
918
985
  specify "should give the database_type as the adapter scheme by default" do
919
- CCC.new.database_type.should == :ccc
986
+ @ccc.new.database_type.should == :ccc
920
987
  end
921
988
 
922
989
  specify "should be instantiated when its scheme is specified" do
923
990
  c = Sequel::Database.connect('ccc://localhost/db')
924
- c.should be_a_kind_of(CCC)
991
+ c.should be_a_kind_of(@ccc)
925
992
  c.opts[:host].should == 'localhost'
926
993
  c.opts[:database].should == 'db'
927
994
  end
928
995
 
929
996
  specify "should be accessible through Sequel.connect" do
930
997
  c = Sequel.connect 'ccc://localhost/db'
931
- c.should be_a_kind_of(CCC)
998
+ c.should be_a_kind_of(@ccc)
932
999
  c.opts[:host].should == 'localhost'
933
1000
  c.opts[:database].should == 'db'
934
1001
  end
@@ -940,7 +1007,7 @@ describe "A Database adapter with a scheme" do
940
1007
  returnValue = 'anything'
941
1008
 
942
1009
  p = proc do |c|
943
- c.should be_a_kind_of(CCC)
1010
+ c.should be_a_kind_of(@ccc)
944
1011
  c.opts[:host].should == 'localhost'
945
1012
  c.opts[:database].should == 'db'
946
1013
  z = y
@@ -948,15 +1015,22 @@ describe "A Database adapter with a scheme" do
948
1015
  x = c
949
1016
  returnValue
950
1017
  end
1018
+
1019
+ @ccc.class_eval do
1020
+ self::DISCONNECTS = []
1021
+ def disconnect
1022
+ self.class::DISCONNECTS << self
1023
+ end
1024
+ end
951
1025
  Sequel::Database.connect('ccc://localhost/db', &p).should == returnValue
952
- CCC::DISCONNECTS.should == [x]
1026
+ @ccc::DISCONNECTS.should == [x]
953
1027
 
954
1028
  Sequel.connect('ccc://localhost/db', &p).should == returnValue
955
- CCC::DISCONNECTS.should == [y, x]
1029
+ @ccc::DISCONNECTS.should == [y, x]
956
1030
 
957
1031
  Sequel.send(:def_adapter_method, :ccc)
958
1032
  Sequel.ccc('db', :host=>'localhost', &p).should == returnValue
959
- CCC::DISCONNECTS.should == [z, y, x]
1033
+ @ccc::DISCONNECTS.should == [z, y, x]
960
1034
  end
961
1035
 
962
1036
  specify "should be accessible through Sequel.<adapter>" do
@@ -967,31 +1041,31 @@ describe "A Database adapter with a scheme" do
967
1041
 
968
1042
  c = Sequel.ccc('mydb')
969
1043
  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}
1044
+ c.should be_a_kind_of(@ccc)
1045
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :adapter_class=>@ccc}
972
1046
 
973
1047
  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}
1048
+ c.should be_a_kind_of(@ccc)
1049
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
976
1050
 
977
1051
  c = Sequel.ccc
978
- c.should be_a_kind_of(CCC)
979
- 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}
980
1054
 
981
1055
  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}
1056
+ c.should be_a_kind_of(@ccc)
1057
+ p.call.should == {:adapter=>:ccc, :database => 'mydb', :host => 'localhost', :adapter_class=>@ccc}
984
1058
  end
985
1059
 
986
1060
  specify "should be accessible through Sequel.connect with options" do
987
1061
  c = Sequel.connect(:adapter => :ccc, :database => 'mydb')
988
- c.should be_a_kind_of(CCC)
1062
+ c.should be_a_kind_of(@ccc)
989
1063
  c.opts[:adapter].should == :ccc
990
1064
  end
991
1065
 
992
1066
  specify "should be accessible through Sequel.connect with URL parameters" do
993
1067
  c = Sequel.connect 'ccc:///db?host=/tmp&user=test'
994
- c.should be_a_kind_of(CCC)
1068
+ c.should be_a_kind_of(@ccc)
995
1069
  c.opts[:host].should == '/tmp'
996
1070
  c.opts[:database].should == 'db'
997
1071
  c.opts[:user].should == 'test'
@@ -999,14 +1073,14 @@ describe "A Database adapter with a scheme" do
999
1073
 
1000
1074
  specify "should have URL parameters take precedence over fixed URL parts" do
1001
1075
  c = Sequel.connect 'ccc://localhost/db?host=a&database=b'
1002
- c.should be_a_kind_of(CCC)
1076
+ c.should be_a_kind_of(@ccc)
1003
1077
  c.opts[:host].should == 'a'
1004
1078
  c.opts[:database].should == 'b'
1005
1079
  end
1006
1080
 
1007
1081
  specify "should have hash options take predence over URL parameters or parts" do
1008
1082
  c = Sequel.connect 'ccc://localhost/db?host=/tmp', :host=>'a', :database=>'b', :user=>'c'
1009
- c.should be_a_kind_of(CCC)
1083
+ c.should be_a_kind_of(@ccc)
1010
1084
  c.opts[:host].should == 'a'
1011
1085
  c.opts[:database].should == 'b'
1012
1086
  c.opts[:user].should == 'c'
@@ -1014,7 +1088,7 @@ describe "A Database adapter with a scheme" do
1014
1088
 
1015
1089
  specify "should unescape values of URL parameters and parts" do
1016
1090
  c = Sequel.connect 'ccc:///d%5bb%5d?host=domain%5cinstance'
1017
- c.should be_a_kind_of(CCC)
1091
+ c.should be_a_kind_of(@ccc)
1018
1092
  c.opts[:database].should == 'd[b]'
1019
1093
  c.opts[:host].should == 'domain\\instance'
1020
1094
  end
@@ -1129,10 +1203,6 @@ describe "A single threaded database" do
1129
1203
  end
1130
1204
 
1131
1205
  describe "A database" do
1132
- before do
1133
- Sequel::Database.single_threaded = false
1134
- end
1135
-
1136
1206
  after do
1137
1207
  Sequel::Database.single_threaded = false
1138
1208
  end
@@ -1177,11 +1247,7 @@ end
1177
1247
 
1178
1248
  describe "Database#fetch" do
1179
1249
  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)}
1250
+ @db = Sequel.mock(:fetch=>proc{|sql| {:sql => sql}})
1185
1251
  end
1186
1252
 
1187
1253
  specify "should create a dataset and invoke its fetch_rows method with the given sql" do
@@ -1228,143 +1294,54 @@ end
1228
1294
 
1229
1295
  describe "Database#[]" do
1230
1296
  before do
1231
- @db = Sequel::Database.new
1297
+ @db = Sequel.mock
1232
1298
  end
1233
1299
 
1234
1300
  specify "should return a dataset when symbols are given" do
1235
1301
  ds = @db[:items]
1236
- ds.class.should == Sequel::Dataset
1302
+ ds.should be_a_kind_of(Sequel::Dataset)
1237
1303
  ds.opts[:from].should == [:items]
1238
1304
  end
1239
1305
 
1240
1306
  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
-
1307
+ @db.fetch = proc{|sql| {:sql=>sql}}
1246
1308
  sql = nil
1247
1309
  @db['select * from xyz where x = ? and y = ?', 15, 'abc'].each {|r| sql = r[:sql]}
1248
1310
  sql.should == "select * from xyz where x = 15 and y = 'abc'"
1249
1311
  end
1250
1312
  end
1251
1313
 
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
1314
  describe "Database#inspect" do
1321
- before do
1322
- @db = DummyDatabase.new
1323
-
1324
- @db.meta_def(:uri) {'blah://blahblah/blah'}
1325
- end
1326
-
1327
1315
  specify "should include the class name and the connection url" do
1328
- @db.inspect.should == '#<DummyDatabase: "blah://blahblah/blah">'
1316
+ Sequel.connect('mock://foo/bar').inspect.should == '#<Sequel::Mock::Database: "mock://foo/bar">'
1329
1317
  end
1330
1318
  end
1331
1319
 
1332
1320
  describe "Database#get" do
1333
1321
  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
1322
+ @db = Sequel.mock(:fetch=>{:a=>1})
1346
1323
  end
1347
1324
 
1348
1325
  specify "should use Dataset#get to get a single value" do
1349
- @db.get(1).should == [1]
1350
- @db.sqls.last.should == 'SELECT 1'
1326
+ @db.get(1).should == 1
1327
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1351
1328
 
1352
1329
  @db.get(:version.sql_function)
1353
- @db.sqls.last.should == 'SELECT version()'
1330
+ @db.sqls.should == ['SELECT version() LIMIT 1']
1354
1331
  end
1355
1332
 
1356
1333
  specify "should accept a block" do
1357
1334
  @db.get{1}
1358
- @db.sqls.last.should == 'SELECT 1'
1335
+ @db.sqls.should == ['SELECT 1 LIMIT 1']
1359
1336
 
1360
1337
  @db.get{version(1)}
1361
- @db.sqls.last.should == 'SELECT version(1)'
1338
+ @db.sqls.should == ['SELECT version(1) LIMIT 1']
1362
1339
  end
1363
1340
  end
1364
1341
 
1365
1342
  describe "Database#call" do
1366
1343
  specify "should call the prepared statement with the given name" do
1367
- db = MockDatabase.new
1344
+ db = Sequel.mock(:fetch=>{:id => 1, :x => 1})
1368
1345
  db[:items].prepare(:select, :select_all)
1369
1346
  db.call(:select_all).should == [{:id => 1, :x => 1}]
1370
1347
  db[:items].filter(:n=>:$n).prepare(:select, :select_n)
@@ -1376,146 +1353,129 @@ end
1376
1353
  describe "Database#server_opts" do
1377
1354
  specify "should return the general opts if no :servers option is used" do
1378
1355
  opts = {:host=>1, :database=>2}
1379
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1356
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1380
1357
  end
1381
1358
 
1382
1359
  specify "should return the general opts if entry for the server is present in the :servers option" do
1383
1360
  opts = {:host=>1, :database=>2, :servers=>{}}
1384
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 1
1361
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 1
1385
1362
  end
1386
1363
 
1387
1364
  specify "should return the general opts merged with the specific opts if given as a hash" do
1388
1365
  opts = {:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}}}
1389
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 3
1366
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 3
1390
1367
  end
1391
1368
 
1392
1369
  specify "should return the sgeneral opts merged with the specific opts if given as a proc" do
1393
1370
  opts = {:host=>1, :database=>2, :servers=>{:server1=>proc{|db| {:host=>4}}}}
1394
- MockDatabase.new(opts).send(:server_opts, :server1)[:host].should == 4
1371
+ Sequel::Database.new(opts).send(:server_opts, :server1)[:host].should == 4
1395
1372
  end
1396
1373
 
1397
1374
  specify "should raise an error if the specific opts is not a proc or hash" do
1398
1375
  opts = {:host=>1, :database=>2, :servers=>{:server1=>2}}
1399
- 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)
1400
1377
  end
1401
1378
  end
1402
1379
 
1403
1380
  describe "Database#add_servers" do
1404
1381
  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
1382
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}})
1411
1383
  end
1412
1384
 
1413
1385
  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}
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}
1417
1389
 
1418
1390
  @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}
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}
1422
1394
 
1423
1395
  @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}
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}
1427
1399
  end
1428
1400
 
1429
1401
  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}
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}
1433
1405
 
1434
1406
  @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}
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}
1438
1410
 
1439
1411
  @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}
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}
1443
1415
  end
1444
1416
  end
1445
1417
 
1446
1418
  describe "Database#remove_servers" do
1447
1419
  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
1420
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1454
1421
  end
1455
1422
 
1456
1423
  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}
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}
1460
1427
 
1461
1428
  @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}
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}
1465
1432
  end
1466
1433
 
1467
1434
  specify "should accept arrays of symbols" do
1468
1435
  @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}
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}
1472
1439
  end
1473
1440
 
1474
1441
  specify "should allow removal while connections are still open" do
1475
1442
  @db.synchronize do |c1|
1476
- c1[:host].should == 1
1443
+ c1.opts[:host].should == 1
1477
1444
  @db.synchronize(:server1) do |c2|
1478
- c2[:host].should == 3
1445
+ c2.opts[:host].should == 3
1479
1446
  @db.synchronize(:server2) do |c3|
1480
- c3[:host].should == 4
1447
+ c3.opts[:host].should == 4
1481
1448
  @db.remove_servers(:server1, :server2)
1482
1449
  @db.synchronize(:server1) do |c4|
1483
1450
  c4.should_not == c2
1484
1451
  c4.should == c1
1485
- c4[:host].should == 1
1452
+ c4.opts[:host].should == 1
1486
1453
  @db.synchronize(:server2) do |c5|
1487
1454
  c5.should_not == c3
1488
1455
  c5.should == c1
1489
- c5[:host].should == 1
1456
+ c5.opts[:host].should == 1
1490
1457
  end
1491
1458
  end
1492
- c3[:host].should == 4
1459
+ c3.opts[:host].should == 4
1493
1460
  end
1494
- c2[:host].should == 3
1461
+ c2.opts[:host].should == 3
1495
1462
  end
1496
- c1[:host].should == 1
1463
+ c1.opts[:host].should == 1
1497
1464
  end
1498
1465
  end
1499
1466
  end
1500
1467
 
1501
1468
  describe "Database#each_server with do/jdbc adapter connection string without :adapter option" do
1502
- before do
1469
+ specify "should yield a separate database object for each server" do
1503
1470
  klass = Class.new(Sequel::Database)
1504
- 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)
1505
1472
  @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
1473
 
1513
- specify "should yield a separate database object for each server" do
1514
1474
  hosts = []
1515
1475
  @db.each_server do |db|
1516
1476
  db.should be_a_kind_of(Sequel::Database)
1517
1477
  db.should_not == @db
1518
- db.opts[:adapter_class].should == MockDatabase
1478
+ db.opts[:adapter_class].should == Sequel::Mock::Database
1519
1479
  db.opts[:database].should == 2
1520
1480
  hosts << db.opts[:host]
1521
1481
  end
@@ -1525,12 +1485,7 @@ end
1525
1485
 
1526
1486
  describe "Database#each_server" do
1527
1487
  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
1488
+ @db = Sequel.mock(:host=>1, :database=>2, :servers=>{:server1=>{:host=>3}, :server2=>{:host=>4}})
1534
1489
  end
1535
1490
 
1536
1491
  specify "should yield a separate database object for each server" do
@@ -1561,21 +1516,25 @@ describe "Database#each_server" do
1561
1516
  end
1562
1517
 
1563
1518
  describe "Database#raise_error" do
1519
+ before do
1520
+ @db = Sequel.mock
1521
+ end
1522
+
1564
1523
  specify "should reraise if the exception class is not in opts[:classes]" do
1565
1524
  e = Class.new(StandardError)
1566
- 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)
1567
1526
  end
1568
1527
 
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)
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)
1571
1530
  end
1572
1531
 
1573
1532
  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)
1533
+ proc{@db.send(:raise_error, Interrupt.new(''))}.should raise_error(Sequel::DatabaseError)
1575
1534
  end
1576
1535
 
1577
1536
  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)
1537
+ proc{@db.send(:raise_error, Interrupt.new(''), :disconnect=>true)}.should raise_error(Sequel::DatabaseDisconnectError)
1579
1538
  end
1580
1539
  end
1581
1540
 
@@ -1594,6 +1553,268 @@ describe "Database#typecast_value" do
1594
1553
  proc{@db.typecast_value(:datetime, 4)}.should raise_error(Sequel::InvalidValue)
1595
1554
  end
1596
1555
 
1556
+ specify "should handle integers with leading 0 as base 10" do
1557
+ @db.typecast_value(:integer, "013").should == 13
1558
+ @db.typecast_value(:integer, "08").should == 8
1559
+ @db.typecast_value(:integer, "000013").should == 13
1560
+ @db.typecast_value(:integer, "000008").should == 8
1561
+ end
1562
+
1563
+ specify "should handle integers with leading 0x as base 16" do
1564
+ @db.typecast_value(:integer, "0x013").should == 19
1565
+ @db.typecast_value(:integer, "0x80").should == 128
1566
+ end
1567
+
1568
+ specify "should typecast blobs as as Sequel::SQL::Blob" do
1569
+ v = @db.typecast_value(:blob, "0x013")
1570
+ v.should be_a_kind_of(Sequel::SQL::Blob)
1571
+ v.should == Sequel::SQL::Blob.new("0x013")
1572
+ @db.typecast_value(:blob, v).object_id.should == v.object_id
1573
+ end
1574
+
1575
+ specify "should typecast boolean values to true, false, or nil" do
1576
+ @db.typecast_value(:boolean, false).should be_false
1577
+ @db.typecast_value(:boolean, 0).should be_false
1578
+ @db.typecast_value(:boolean, "0").should be_false
1579
+ @db.typecast_value(:boolean, 'f').should be_false
1580
+ @db.typecast_value(:boolean, 'false').should be_false
1581
+ @db.typecast_value(:boolean, true).should be_true
1582
+ @db.typecast_value(:boolean, 1).should be_true
1583
+ @db.typecast_value(:boolean, '1').should be_true
1584
+ @db.typecast_value(:boolean, 't').should be_true
1585
+ @db.typecast_value(:boolean, 'true').should be_true
1586
+ @db.typecast_value(:boolean, '').should be_nil
1587
+ end
1588
+
1589
+ specify "should typecast date values to Date" do
1590
+ @db.typecast_value(:date, Date.today).should == Date.today
1591
+ @db.typecast_value(:date, DateTime.now).should == Date.today
1592
+ @db.typecast_value(:date, Time.now).should == Date.today
1593
+ @db.typecast_value(:date, Date.today.to_s).should == Date.today
1594
+ @db.typecast_value(:date, :year=>Date.today.year, :month=>Date.today.month, :day=>Date.today.day).should == Date.today
1595
+ end
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
+
1640
+ specify "should typecast datetime values to Sequel.datetime_class with correct timezone handling" do
1641
+ t = Time.utc(2011, 1, 2, 3, 4, 5) # UTC Time
1642
+ t2 = Time.mktime(2011, 1, 2, 3, 4, 5) # Local Time
1643
+ t3 = Time.utc(2011, 1, 2, 3, 4, 5) - (t - t2) # Local Time in UTC Time
1644
+ t4 = Time.mktime(2011, 1, 2, 3, 4, 5) + (t - t2) # UTC Time in Local Time
1645
+ dt = DateTime.civil(2011, 1, 2, 3, 4, 5)
1646
+ r1 = defined?(Rational) ? Rational(t2.utc_offset, 86400) : t2.utc_offset/86400.0
1647
+ r2 = defined?(Rational) ? Rational((t - t2).to_i, 86400) : (t - t2).to_i/86400.0
1648
+ dt2 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1)
1649
+ dt3 = DateTime.civil(2011, 1, 2, 3, 4, 5) - r2
1650
+ dt4 = DateTime.civil(2011, 1, 2, 3, 4, 5, r1) + r2
1651
+
1652
+ t.should == t4
1653
+ t2.should == t3
1654
+ dt.should == dt4
1655
+ dt2.should == dt3
1656
+
1657
+ check = proc do |i, o|
1658
+ v = @db.typecast_value(:datetime, i)
1659
+ v.should == o
1660
+ if o.is_a?(Time)
1661
+ v.utc_offset.should == o.utc_offset
1662
+ else
1663
+ v.offset.should == o.offset
1664
+ end
1665
+ end
1666
+ begin
1667
+ @db.typecast_value(:datetime, dt).should == t
1668
+ @db.typecast_value(:datetime, dt2).should == t2
1669
+ @db.typecast_value(:datetime, t).should == t
1670
+ @db.typecast_value(:datetime, t2).should == t2
1671
+ @db.typecast_value(:datetime, dt.to_s).should == t
1672
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == t2
1673
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == Time.mktime(2011, 1, 2, 0, 0, 0)
1674
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == t2
1675
+
1676
+ Sequel.datetime_class = DateTime
1677
+ @db.typecast_value(:datetime, dt).should == dt
1678
+ @db.typecast_value(:datetime, dt2).should == dt2
1679
+ @db.typecast_value(:datetime, t).should == dt
1680
+ @db.typecast_value(:datetime, t2).should == dt2
1681
+ @db.typecast_value(:datetime, dt.to_s).should == dt
1682
+ @db.typecast_value(:datetime, dt.strftime('%F %T')).should == dt
1683
+ @db.typecast_value(:datetime, Date.civil(2011, 1, 2)).should == DateTime.civil(2011, 1, 2, 0, 0, 0)
1684
+ @db.typecast_value(:datetime, :year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec).should == dt
1685
+
1686
+ Sequel.application_timezone = :utc
1687
+ Sequel.typecast_timezone = :local
1688
+ Sequel.datetime_class = Time
1689
+ check[dt, t]
1690
+ check[dt2, t3]
1691
+ check[t, t]
1692
+ check[t2, t3]
1693
+ check[dt.to_s, t]
1694
+ check[dt.strftime('%F %T'), t3]
1695
+ check[Date.civil(2011, 1, 2), Time.utc(2011, 1, 2, 0, 0, 0)]
1696
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, t3]
1697
+
1698
+ Sequel.datetime_class = DateTime
1699
+ check[dt, dt]
1700
+ check[dt2, dt3]
1701
+ check[t, dt]
1702
+ check[t2, dt3]
1703
+ check[dt.to_s, dt]
1704
+ check[dt.strftime('%F %T'), dt3]
1705
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0)]
1706
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt3]
1707
+
1708
+ Sequel.typecast_timezone = :utc
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'), t]
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}, t]
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'), dt]
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}, dt]
1728
+
1729
+ Sequel.application_timezone = :local
1730
+ Sequel.datetime_class = Time
1731
+ check[dt, t4]
1732
+ check[dt2, t2]
1733
+ check[t, t4]
1734
+ check[t2, t2]
1735
+ check[dt.to_s, t4]
1736
+ check[dt.strftime('%F %T'), t4]
1737
+ check[Date.civil(2011, 1, 2), Time.local(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}, t4]
1739
+
1740
+ Sequel.datetime_class = DateTime
1741
+ check[dt, dt4]
1742
+ check[dt2, dt2]
1743
+ check[t, dt4]
1744
+ check[t2, dt2]
1745
+ check[dt.to_s, dt4]
1746
+ check[dt.strftime('%F %T'), dt4]
1747
+ check[Date.civil(2011, 1, 2), DateTime.civil(2011, 1, 2, 0, 0, 0, r1)]
1748
+ check[{:year=>dt.year, :month=>dt.month, :day=>dt.day, :hour=>dt.hour, :minute=>dt.min, :second=>dt.sec}, dt4]
1749
+
1750
+ Sequel.typecast_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'), t2]
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}, t2]
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'), dt2]
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}, dt2]
1770
+
1771
+ ensure
1772
+ Sequel.default_timezone = nil
1773
+ Sequel.datetime_class = Time
1774
+ end
1775
+ end
1776
+
1777
+ specify "should typecast decimal values to BigDecimal" do
1778
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1779
+ v = @db.typecast_value(:decimal, i)
1780
+ v.should be_a_kind_of(BigDecimal)
1781
+ v.should == BigDecimal.new('1.0')
1782
+ end
1783
+ end
1784
+
1785
+ specify "should typecast float values to Float" do
1786
+ [1.0, 1, '1.0', BigDecimal('1.0')].each do |i|
1787
+ v = @db.typecast_value(:float, i)
1788
+ v.should be_a_kind_of(Float)
1789
+ v.should == 1.0
1790
+ end
1791
+ end
1792
+
1793
+ specify "should typecast string values to String" do
1794
+ [1.0, '1.0', '1.0'.to_sequel_blob].each do |i|
1795
+ v = @db.typecast_value(:string, i)
1796
+ v.should be_an_instance_of(String)
1797
+ v.should == "1.0"
1798
+ end
1799
+ end
1800
+
1801
+ specify "should typecast time values to SQLTime" do
1802
+ t = Time.now
1803
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3)
1804
+ [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|
1805
+ v = @db.typecast_value(:time, i)
1806
+ v.should be_an_instance_of(Sequel::SQLTime)
1807
+ v.should == st
1808
+ end
1809
+ end
1810
+
1811
+ specify "should correctly handle time value conversion to SQLTime with fractional seconds" do
1812
+ t = Time.now
1813
+ st = Sequel::SQLTime.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1814
+ t = Time.local(t.year, t.month, t.day, 1, 2, 3, 500000)
1815
+ @db.typecast_value(:time, t).should == st
1816
+ end
1817
+
1597
1818
  specify "should have an underlying exception class available at wrapped_exception" do
1598
1819
  begin
1599
1820
  @db.typecast_value(:date, 'a')
@@ -1639,7 +1860,7 @@ describe "Database#blank_object?" do
1639
1860
  end
1640
1861
 
1641
1862
  describe "Database#schema_autoincrementing_primary_key?" do
1642
- 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
1643
1864
  m = Sequel::Database.new.method(:schema_autoincrementing_primary_key?)
1644
1865
  m.call(:primary_key=>true).should == true
1645
1866
  m.call(:primary_key=>false).should == false
@@ -1695,7 +1916,7 @@ describe "Database#column_schema_to_ruby_default" do
1695
1916
  db = Sequel::Database.new
1696
1917
  m = db.method(:column_schema_to_ruby_default)
1697
1918
  p = lambda{|d,t| m.call(d,t)}
1698
- p[nil, :integer].should == nil
1919
+ p[nil, :integer].should be_nil
1699
1920
  p['1', :integer].should == 1
1700
1921
  p['-1', :integer].should == -1
1701
1922
  p['1.0', :float].should == 1.0
@@ -1715,12 +1936,12 @@ describe "Database#column_schema_to_ruby_default" do
1715
1936
  p["'\\a''b'", :string].should == "\\a'b"
1716
1937
  p["'NULL'", :string].should == "NULL"
1717
1938
  p["'2009-10-29'", :date].should == Date.new(2009,10,29)
1718
- p["CURRENT_TIMESTAMP", :date].should == nil
1719
- p["today()", :date].should == nil
1939
+ p["CURRENT_TIMESTAMP", :date].should be_nil
1940
+ p["today()", :date].should be_nil
1720
1941
  p["'2009-10-29T10:20:30-07:00'", :datetime].should == DateTime.parse('2009-10-29T10:20:30-07:00')
1721
1942
  p["'2009-10-29 10:20:30'", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1722
1943
  p["'10:20:30'", :time].should == Time.parse('10:20:30')
1723
- p["NaN", :float].should == nil
1944
+ p["NaN", :float].should be_nil
1724
1945
 
1725
1946
  db.meta_def(:database_type){:postgres}
1726
1947
  p["''::text", :string].should == ""
@@ -1744,8 +1965,8 @@ describe "Database#column_schema_to_ruby_default" do
1744
1965
  p["2009-10-29", :date].should == Date.new(2009,10,29)
1745
1966
  p["2009-10-29 10:20:30", :datetime].should == DateTime.parse('2009-10-29 10:20:30')
1746
1967
  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
1968
+ p["CURRENT_DATE", :date].should be_nil
1969
+ p["CURRENT_TIMESTAMP", :datetime].should be_nil
1749
1970
  p["a", :enum].should == "a"
1750
1971
 
1751
1972
  db.meta_def(:database_type){:mssql}