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