sequel 3.43.0 → 3.44.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 (73) hide show
  1. data/CHANGELOG +32 -0
  2. data/doc/association_basics.rdoc +10 -3
  3. data/doc/release_notes/3.37.0.txt +1 -1
  4. data/doc/release_notes/3.44.0.txt +152 -0
  5. data/lib/sequel/adapters/ado.rb +0 -6
  6. data/lib/sequel/adapters/db2.rb +0 -3
  7. data/lib/sequel/adapters/dbi.rb +0 -6
  8. data/lib/sequel/adapters/ibmdb.rb +0 -3
  9. data/lib/sequel/adapters/jdbc.rb +8 -12
  10. data/lib/sequel/adapters/jdbc/as400.rb +2 -18
  11. data/lib/sequel/adapters/jdbc/derby.rb +10 -0
  12. data/lib/sequel/adapters/jdbc/h2.rb +10 -0
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +10 -0
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -4
  16. data/lib/sequel/adapters/mock.rb +5 -0
  17. data/lib/sequel/adapters/mysql2.rb +4 -13
  18. data/lib/sequel/adapters/odbc.rb +0 -5
  19. data/lib/sequel/adapters/oracle.rb +3 -5
  20. data/lib/sequel/adapters/postgres.rb +2 -1
  21. data/lib/sequel/adapters/shared/access.rb +10 -0
  22. data/lib/sequel/adapters/shared/cubrid.rb +9 -0
  23. data/lib/sequel/adapters/shared/db2.rb +10 -0
  24. data/lib/sequel/adapters/shared/mssql.rb +10 -0
  25. data/lib/sequel/adapters/shared/mysql.rb +14 -0
  26. data/lib/sequel/adapters/shared/oracle.rb +15 -0
  27. data/lib/sequel/adapters/shared/postgres.rb +26 -2
  28. data/lib/sequel/adapters/shared/sqlite.rb +15 -0
  29. data/lib/sequel/adapters/tinytds.rb +5 -32
  30. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -37
  31. data/lib/sequel/core.rb +3 -3
  32. data/lib/sequel/database/misc.rb +40 -4
  33. data/lib/sequel/database/query.rb +1 -1
  34. data/lib/sequel/database/schema_methods.rb +33 -12
  35. data/lib/sequel/dataset/actions.rb +51 -2
  36. data/lib/sequel/dataset/features.rb +0 -6
  37. data/lib/sequel/dataset/sql.rb +1 -1
  38. data/lib/sequel/exceptions.rb +22 -7
  39. data/lib/sequel/extensions/columns_introspection.rb +30 -5
  40. data/lib/sequel/extensions/pg_auto_parameterize.rb +9 -0
  41. data/lib/sequel/model/associations.rb +50 -37
  42. data/lib/sequel/model/base.rb +30 -1
  43. data/lib/sequel/plugins/eager_each.rb +17 -21
  44. data/lib/sequel/plugins/identity_map.rb +2 -1
  45. data/lib/sequel/plugins/many_through_many.rb +1 -1
  46. data/lib/sequel/plugins/single_table_inheritance.rb +2 -2
  47. data/lib/sequel/plugins/tactical_eager_loading.rb +1 -1
  48. data/lib/sequel/sql.rb +4 -2
  49. data/lib/sequel/version.rb +1 -1
  50. data/spec/adapters/postgres_spec.rb +32 -2
  51. data/spec/adapters/sqlite_spec.rb +20 -0
  52. data/spec/core/database_spec.rb +40 -0
  53. data/spec/core/dataset_spec.rb +91 -4
  54. data/spec/core/mock_adapter_spec.rb +2 -1
  55. data/spec/core/schema_generator_spec.rb +4 -0
  56. data/spec/core/schema_spec.rb +9 -3
  57. data/spec/extensions/association_dependencies_spec.rb +3 -3
  58. data/spec/extensions/columns_introspection_spec.rb +28 -2
  59. data/spec/extensions/eager_each_spec.rb +0 -1
  60. data/spec/extensions/identity_map_spec.rb +3 -2
  61. data/spec/extensions/migration_spec.rb +6 -0
  62. data/spec/extensions/prepared_statements_associations_spec.rb +2 -2
  63. data/spec/extensions/rcte_tree_spec.rb +2 -2
  64. data/spec/extensions/single_table_inheritance_spec.rb +3 -0
  65. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  66. data/spec/extensions/validation_class_methods_spec.rb +8 -0
  67. data/spec/integration/associations_test.rb +4 -4
  68. data/spec/integration/database_test.rb +68 -20
  69. data/spec/integration/dataset_test.rb +48 -0
  70. data/spec/integration/schema_test.rb +25 -1
  71. data/spec/model/associations_spec.rb +21 -8
  72. data/spec/model/dataset_methods_spec.rb +58 -18
  73. metadata +4 -2
@@ -439,8 +439,9 @@ describe "Sequel Mock Adapter" do
439
439
  end
440
440
  end
441
441
 
442
- specify "should automatically set version for postgres" do
442
+ specify "should automatically set version for postgres and mssql" do
443
443
  Sequel.mock(:host=>'postgres').server_version.should == 90103
444
+ Sequel.mock(:host=>'mssql').server_version.should == 10000000
444
445
  end
445
446
 
446
447
  specify "should stub out the primary_key method for postgres" do
@@ -19,6 +19,10 @@ describe Sequel::Schema::Generator do
19
19
  @columns, @indexes, @constraints = @generator.columns, @generator.indexes, @generator.constraints
20
20
  end
21
21
 
22
+ it "should respond to everything" do
23
+ @generator.respond_to?(:foo).should be_true
24
+ end if RUBY_VERSION >= '1.9'
25
+
22
26
  it "should primary key column first" do
23
27
  @columns.first[:name].should == :id
24
28
  @columns.first[:primary_key].should == true
@@ -1219,14 +1219,20 @@ describe "Database#create_view" do
1219
1219
  @db.create_view :test, @db[:items].select(:a, :b).order(:c)
1220
1220
  @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1221
1221
  @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1222
- @db.sqls.should == ['CREATE OR REPLACE VIEW sch.test AS SELECT * FROM xyz']
1222
+ @db.sqls.should == ['DROP VIEW sch.test', 'CREATE VIEW sch.test AS SELECT * FROM xyz']
1223
1223
  end
1224
1224
 
1225
1225
  specify "should construct proper SQL with dataset" do
1226
1226
  @db.create_or_replace_view :test, @db[:items].select(:a, :b).order(:c)
1227
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1227
+ @db.sqls.should == ['DROP VIEW test', 'CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1228
1228
  @db.create_or_replace_view Sequel.identifier(:test), @db[:items].select(:a, :b).order(:c)
1229
- @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT a, b FROM items ORDER BY c']
1229
+ @db.sqls.should == ['DROP VIEW test', 'CREATE VIEW test AS SELECT a, b FROM items ORDER BY c']
1230
+ end
1231
+
1232
+ specify "should use CREATE OR REPLACE VIEW if such syntax is supported" do
1233
+ def @db.supports_create_or_replace_view?() true end
1234
+ @db.create_or_replace_view :test, @db[:items]
1235
+ @db.sqls.should == ['CREATE OR REPLACE VIEW test AS SELECT * FROM items']
1230
1236
  end
1231
1237
  end
1232
1238
 
@@ -33,13 +33,13 @@ describe "AssociationDependencies plugin" do
33
33
  specify "should allow destroying associated one_to_one associated object" do
34
34
  @Artist.add_association_dependencies :first_album=>:destroy
35
35
  @Artist.load(:id=>2, :name=>'Ar').destroy
36
- MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
36
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((position = 1) AND (albums.artist_id = 2)) LIMIT 1', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
37
37
  end
38
38
 
39
39
  specify "should allow deleting associated one_to_one associated object" do
40
40
  @Artist.add_association_dependencies :first_album=>:delete
41
41
  @Artist.load(:id=>2, :name=>'Ar').destroy
42
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
42
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((position = 1) AND (albums.artist_id = 2))', 'DELETE FROM artists WHERE id = 2']
43
43
  end
44
44
 
45
45
  specify "should allow destroying associated one_to_many objects" do
@@ -57,7 +57,7 @@ describe "AssociationDependencies plugin" do
57
57
  specify "should allow nullifying associated one_to_one objects" do
58
58
  @Artist.add_association_dependencies :first_album=>:nullify
59
59
  @Artist.load(:id=>2, :name=>'Ar').destroy
60
- MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
60
+ MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((position = 1) AND (artist_id = 2))', 'DELETE FROM artists WHERE id = 2']
61
61
  end
62
62
 
63
63
  specify "should allow nullifying associated one_to_many objects" do
@@ -25,10 +25,10 @@ end
25
25
 
26
26
  describe "columns_introspection extension" do
27
27
  before do
28
- @db = MODEL_DB
28
+ @db = Sequel.mock
29
29
  @ds = @db[:a]
30
30
  @ds.extend(Sequel::ColumnsIntrospection.dup) # dup to allow multiple places in class hierarchy
31
- @db.reset
31
+ @db.sqls
32
32
  end
33
33
 
34
34
  specify "should not issue a database query if the columns are already loaded" do
@@ -74,6 +74,32 @@ describe "columns_introspection extension" do
74
74
  @db.sqls.length.should == 0
75
75
  end
76
76
 
77
+ specify "should handle selecting * from a single subselect with no joins without a database query if the subselect's columns can be handled" do
78
+ @ds.select(:x).from_self.columns.should == [:x]
79
+ @db.sqls.length.should == 0
80
+ @ds.select(:x).from_self.from_self.columns.should == [:x]
81
+ @db.sqls.length.should == 0
82
+ end
83
+
84
+ specify "should handle selecting * from a single table with no joins without a database query if the database has cached schema columns for the table" do
85
+ @db.instance_variable_set(:@schemas, "a"=>[[:x, {}]])
86
+ @ds.columns.should == [:x]
87
+ @db.sqls.length.should == 0
88
+ end
89
+
90
+ specify "should issue a database query for multiple subselects or joins" do
91
+ @ds.from(@ds.select(:x), @ds.select(:y)).columns
92
+ @db.sqls.length.should == 1
93
+ @ds.select(:x).from_self.natural_join(:a).columns
94
+ @db.sqls.length.should == 1
95
+ end
96
+
97
+ specify "should issue a database query when common table expressions are used" do
98
+ @db.instance_variable_set(:@schemas, "a"=>[[:x, {}]])
99
+ @ds.with(:a, @ds).columns
100
+ @db.sqls.length.should == 1
101
+ end
102
+
77
103
  specify "should issue a database query if the wildcard is selected" do
78
104
  @ds.columns
79
105
  @db.sqls.length.should == 1
@@ -30,5 +30,4 @@ describe "Sequel::Plugins::EagerEach" do
30
30
  a.map{|c| c.associations[:children]}.should == [[@c.load(:id=>3, :parent_id=>1), @c.load(:id=>4, :parent_id=>1)], [@c.load(:id=>5, :parent_id=>2), @c.load(:id=>6, :parent_id=>2)]]
31
31
  @c.db.sqls.should == ['SELECT items.id, items.parent_id, children.id AS children_id, children.parent_id AS children_parent_id FROM items LEFT OUTER JOIN items AS children ON (children.parent_id = items.id)']
32
32
  end
33
-
34
33
  end
@@ -282,9 +282,10 @@ describe "Sequel::Plugins::IdentityMap" do
282
282
 
283
283
  it "should work correctly when eagerly loading many_to_many associations with composite keys" do
284
284
  @c1.columns :id, :id2
285
- @c2.columns :id
285
+ @c2.columns :id, :id2
286
286
  @c1.set_primary_key :id, :id2
287
- @c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>:artist_id, :join_table=>:aa
287
+ @c2.set_primary_key :id, :id2
288
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>[:artist_id1, :artist_id2], :join_table=>:aa
288
289
  @c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
289
290
  @c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
290
291
 
@@ -55,6 +55,12 @@ describe "Migration.apply" do
55
55
  m.apply(@db, :up).should == nil
56
56
  m.apply(@db, :down).should == nil
57
57
  end
58
+
59
+ specify "should respond to the methods the database responds to" do
60
+ m = Sequel::Migration.new(Sequel.mock)
61
+ m.respond_to?(:foo).should be_false
62
+ m.respond_to?(:execute).should be_true
63
+ end if RUBY_VERSION >= '1.9'
58
64
  end
59
65
 
60
66
  describe "SimpleMigration#apply" do
@@ -92,7 +92,7 @@ describe "Sequel::Plugins::AssociationPks" do
92
92
  specify "should run a regular query if :conditions option is used when defining the association" do
93
93
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :conditions=>{:a=>1}
94
94
  @Artist.load(:id=>1).albums
95
- @db.sqls.should == ["SELECT * FROM albums WHERE ((albums.artist_id = 1) AND (a = 1))"]
95
+ @db.sqls.should == ["SELECT * FROM albums WHERE ((a = 1) AND (albums.artist_id = 1))"]
96
96
  end
97
97
 
98
98
  specify "should run a regular query if :dataset option is used when defining the association" do
@@ -106,6 +106,6 @@ describe "Sequel::Plugins::AssociationPks" do
106
106
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :conditions=>{:a=>1}
107
107
  @Artist.one_to_many :oalbums, :clone=>:albums
108
108
  @Artist.load(:id=>1).oalbums
109
- @db.sqls.should == ["SELECT * FROM albums WHERE ((albums.artist_id = 1) AND (a = 1))"]
109
+ @db.sqls.should == ["SELECT * FROM albums WHERE ((a = 1) AND (albums.artist_id = 1))"]
110
110
  end
111
111
  end
@@ -49,8 +49,8 @@ describe Sequel::Model, "rcte_tree" do
49
49
 
50
50
  it "should use the correct SQL for lazy associations with :conditions option" do
51
51
  @c.plugin :rcte_tree, :conditions => {:i => 1}
52
- @o.parent_dataset.sql.should == 'SELECT * FROM nodes WHERE ((nodes.id = 1) AND (i = 1)) LIMIT 1'
53
- @o.children_dataset.sql.should == 'SELECT * FROM nodes WHERE ((nodes.parent_id = 2) AND (i = 1))'
52
+ @o.parent_dataset.sql.should == 'SELECT * FROM nodes WHERE ((i = 1) AND (nodes.id = 1)) LIMIT 1'
53
+ @o.children_dataset.sql.should == 'SELECT * FROM nodes WHERE ((i = 1) AND (nodes.parent_id = 2))'
54
54
  @o.ancestors_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE ((id = 1) AND (i = 1)) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.parent_id = nodes.id) WHERE (i = 1)) SELECT * FROM t AS nodes WHERE (i = 1)'
55
55
  @o.descendants_dataset.sql.should == 'WITH t AS (SELECT * FROM nodes WHERE ((parent_id = 2) AND (i = 1)) UNION ALL SELECT nodes.* FROM nodes INNER JOIN t ON (t.id = nodes.parent_id) WHERE (i = 1)) SELECT * FROM t AS nodes WHERE (i = 1)'
56
56
  end
@@ -158,6 +158,9 @@ describe Sequel::Model, "#sti_key" do
158
158
  StiTest2.create.kind.should == 4
159
159
  StiTest3.create.kind.should == 5
160
160
  StiTest4.create.kind.should == 6
161
+
162
+ class ::StiTest5 < ::StiTest4; end
163
+ StiTest5.create.kind.should == nil
161
164
  end
162
165
 
163
166
  it "should infer key_map from model_map if provided as a hash" do
@@ -25,7 +25,7 @@ describe "Sequel::Plugins::TacticalEagerLoading" do
25
25
  Object.send(:remove_const, :TaticalEagerLoadingModel)
26
26
  end
27
27
 
28
- it "Dataset#all should set the retrieved_by and reteived_with attributes" do
28
+ it "Dataset#all should set the retrieved_by and retrieved_with attributes" do
29
29
  ts = @c.all
30
30
  ts.map{|x| [x.retrieved_by, x.retrieved_with]}.should == [[@ds,ts], [@ds,ts], [@ds,ts], [@ds,ts]]
31
31
  end
@@ -138,6 +138,14 @@ describe Sequel::Model do
138
138
  o.valid?.should == false
139
139
  o.errors.full_messages.should == ['blah is not cool']
140
140
  end
141
+
142
+ specify "should have the validates block have appropriate respond_to?" do
143
+ c = nil
144
+ @c.validates{c = respond_to?(:foo)}
145
+ c.should be_false
146
+ @c.validates{c = respond_to?(:length_of)}
147
+ c.should be_true
148
+ end if RUBY_VERSION >= '1.9'
141
149
  end
142
150
 
143
151
  describe Sequel::Model do
@@ -51,8 +51,8 @@ shared_examples_for "eager limit strategies" do
51
51
  end
52
52
 
53
53
  specify "should correctly handle limits and offsets when eager loading many_to_many associations" do
54
- if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
55
- pending("Derby errors with correlated subqueries on many_to_many associations")
54
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby, :mssql)
55
+ pending("correlated subqueries on many_to_many associations not supported")
56
56
  end
57
57
  Album.many_to_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
58
58
  Album.many_to_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
@@ -76,8 +76,8 @@ shared_examples_for "eager limit strategies" do
76
76
  end
77
77
 
78
78
  specify "should correctly handle limits and offsets when eager loading many_through_many associations" do
79
- if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
80
- pending("Derby errors with correlated subqueries on many_through_many associations")
79
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby, :mssql)
80
+ pending("correlated subqueries on many_through_many associations not supported")
81
81
  end
82
82
  Artist.many_through_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
83
83
  Artist.many_through_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
@@ -1,28 +1,76 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
3
  describe Sequel::Database do
4
+ before do
5
+ @db = INTEGRATION_DB
6
+ end
7
+
4
8
  specify "should provide disconnect functionality" do
5
- INTEGRATION_DB.disconnect
6
- INTEGRATION_DB.pool.size.should == 0
7
- INTEGRATION_DB.test_connection
8
- INTEGRATION_DB.pool.size.should == 1
9
+ @db.disconnect
10
+ @db.pool.size.should == 0
11
+ @db.test_connection
12
+ @db.pool.size.should == 1
9
13
  end
10
14
 
11
15
  specify "should provide disconnect functionality after preparing a statement" do
12
- INTEGRATION_DB.create_table!(:items){Integer :i}
13
- INTEGRATION_DB[:items].prepare(:first, :a).call
14
- INTEGRATION_DB.disconnect
15
- INTEGRATION_DB.pool.size.should == 0
16
- INTEGRATION_DB.drop_table?(:items)
16
+ @db.create_table!(:items){Integer :i}
17
+ @db[:items].prepare(:first, :a).call
18
+ @db.disconnect
19
+ @db.pool.size.should == 0
20
+ @db.drop_table?(:items)
17
21
  end
18
22
 
19
23
  specify "should raise Sequel::DatabaseError on invalid SQL" do
20
- proc{INTEGRATION_DB << "SELECT"}.should raise_error(Sequel::DatabaseError)
24
+ proc{@db << "SELECT"}.should raise_error(Sequel::DatabaseError)
25
+ end
26
+
27
+ describe "constraint violations" do
28
+ before do
29
+ @db.drop_table?(:test2, :test)
30
+ end
31
+ after do
32
+ @db.drop_table?(:test2, :test)
33
+ end
34
+
35
+ cspecify "should raise Sequel::UniqueConstraintViolation when a unique constraint is violated", [:jdbc, :sqlite], [:db2] do
36
+ @db.create_table!(:test){String :a, :unique=>true, :null=>false}
37
+ @db[:test].insert('1')
38
+ proc{@db[:test].insert('1')}.should raise_error(Sequel::UniqueConstraintViolation)
39
+ @db[:test].insert('2')
40
+ proc{@db[:test].update(:a=>'1')}.should raise_error(Sequel::UniqueConstraintViolation)
41
+ end
42
+
43
+ cspecify "should raise Sequel::CheckConstraintViolation when a check constraint is violated", :mysql, :sqlite, [:db2] do
44
+ @db.create_table!(:test){String :a; check Sequel.~(:a=>'1')}
45
+ proc{@db[:test].insert('1')}.should raise_error(Sequel::CheckConstraintViolation)
46
+ @db[:test].insert('2')
47
+ proc{@db[:test].insert('1')}.should raise_error(Sequel::CheckConstraintViolation)
48
+ end
49
+
50
+ cspecify "should raise Sequel::ForeignKeyConstraintViolation when a foreign key constraint is violated", [:jdbc, :sqlite], [:db2] do
51
+ @db.create_table!(:test, :engine=>:InnoDB){primary_key :id}
52
+ @db.create_table!(:test2, :engine=>:InnoDB){foreign_key :tid, :test}
53
+ proc{@db[:test2].insert(:tid=>1)}.should raise_error(Sequel::ForeignKeyConstraintViolation)
54
+ @db[:test].insert
55
+ @db[:test2].insert(:tid=>1)
56
+ proc{@db[:test2].where(:tid=>1).update(:tid=>3)}.should raise_error(Sequel::ForeignKeyConstraintViolation)
57
+ proc{@db[:test].where(:id=>1).delete}.should raise_error(Sequel::ForeignKeyConstraintViolation)
58
+ end
59
+
60
+ cspecify "should raise Sequel::NotNullConstraintViolation when a not null constraint is violated", [:jdbc, :sqlite], [:db2] do
61
+ @db.create_table!(:test){Integer :a, :null=>false}
62
+ proc{@db[:test].insert(:a=>nil)}.should raise_error(Sequel::NotNullConstraintViolation)
63
+ unless @db.database_type == :mysql
64
+ # Broken mysql silently changes NULL here to 0, and doesn't raise an exception.
65
+ @db[:test].insert(2)
66
+ proc{@db[:test].update(:a=>nil)}.should raise_error(Sequel::NotNullConstraintViolation)
67
+ end
68
+ end
21
69
  end
22
70
 
23
71
  specify "should store underlying wrapped exception in Sequel::DatabaseError" do
24
72
  begin
25
- INTEGRATION_DB << "SELECT"
73
+ @db << "SELECT"
26
74
  rescue Sequel::DatabaseError=>e
27
75
  if defined?(Java::JavaLang::Exception)
28
76
  (e.wrapped_exception.is_a?(Exception) || e.wrapped_exception.is_a?(Java::JavaLang::Exception)).should be_true
@@ -33,20 +81,20 @@ describe Sequel::Database do
33
81
  end
34
82
 
35
83
  specify "should not have the connection pool swallow non-StandardError based exceptions" do
36
- proc{INTEGRATION_DB.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
84
+ proc{@db.pool.hold{raise Interrupt, "test"}}.should raise_error(Interrupt)
37
85
  end
38
86
 
39
87
  specify "should be able to disconnect connections more than once without exceptions" do
40
- conn = INTEGRATION_DB.synchronize{|c| c}
41
- INTEGRATION_DB.disconnect
42
- INTEGRATION_DB.disconnect_connection(conn)
43
- INTEGRATION_DB.disconnect_connection(conn)
88
+ conn = @db.synchronize{|c| c}
89
+ @db.disconnect
90
+ @db.disconnect_connection(conn)
91
+ @db.disconnect_connection(conn)
44
92
  end
45
93
 
46
94
  cspecify "should provide ability to check connections for validity", [:do, :postgres] do
47
- conn = INTEGRATION_DB.synchronize{|c| c}
48
- INTEGRATION_DB.valid_connection?(conn).should be_true
49
- INTEGRATION_DB.disconnect
50
- INTEGRATION_DB.valid_connection?(conn).should be_false
95
+ conn = @db.synchronize{|c| c}
96
+ @db.valid_connection?(conn).should be_true
97
+ @db.disconnect
98
+ @db.valid_connection?(conn).should be_false
51
99
  end
52
100
  end
@@ -98,6 +98,28 @@ describe "Simple Dataset operations" do
98
98
  @ds.all.should == [{:id=>1, :number=>10}]
99
99
  end
100
100
 
101
+ specify "should iterate over records as they come in" do
102
+ called = false
103
+ @ds.each{|row| called = true; row.should == {:id=>1, :number=>10}}
104
+ called.should == true
105
+ end
106
+
107
+ specify "should support iterating over large numbers of records with paged_each" do
108
+ (2..100).each{|i| @ds.insert(:number=>i*10)}
109
+
110
+ rows = []
111
+ @ds.order(:number).paged_each(:rows_per_fetch=>5){|row| rows << row}
112
+ rows.should == (1..100).map{|i| {:id=>i, :number=>i*10}}
113
+
114
+ rows = []
115
+ @ds.order(:number).paged_each(:rows_per_fetch=>3){|row| rows << row}
116
+ rows.should == (1..100).map{|i| {:id=>i, :number=>i*10}}
117
+
118
+ rows = []
119
+ @ds.order(:number).limit(50, 25).paged_each(:rows_per_fetch=>3){|row| rows << row}
120
+ rows.should == (26..75).map{|i| {:id=>i, :number=>i*10}}
121
+ end
122
+
101
123
  specify "should fetch all results correctly" do
102
124
  @ds.all.should == [{:id=>1, :number=>10}]
103
125
  end
@@ -170,6 +192,32 @@ describe "Simple Dataset operations" do
170
192
  @ds.where(:id=>@ds.select(:id).order(:id).limit(2, 1)).all.should == [{:id=>2, :number=>20}]
171
193
  end
172
194
 
195
+ specify "should fetch correctly when using limit and offset in a from_self" do
196
+ @ds.insert(:number=>20)
197
+ ds = @ds.order(:id).limit(1, 1).from_self
198
+ ds.all.should == [{:number=>20, :id=>2}]
199
+ ds.columns.should == [:id, :number]
200
+ @ds.order(:id).limit(1, 1).columns.should == [:id, :number]
201
+ end
202
+
203
+ specify "should fetch correctly when using nested limit and offset in a from_self" do
204
+ @ds.insert(:number=>20)
205
+ @ds.insert(:number=>30)
206
+ ds = @ds.order(:id).limit(2, 1).from_self.reverse_order(:number).limit(1, 1)
207
+ ds.all.should == [{:number=>20, :id=>2}]
208
+ ds.columns.should == [:id, :number]
209
+ @ds.order(:id).limit(2, 1).from_self.reverse_order(:number).limit(1, 1).columns.should == [:id, :number]
210
+
211
+ ds = @ds.order(:id).limit(3, 1).from_self.limit(2, 1).from_self.limit(1, 1)
212
+ ds.all.should == []
213
+ ds.columns.should == [:id, :number]
214
+
215
+ @ds.insert(:number=>40)
216
+ ds = @ds.order(:id).limit(3, 1).from_self.reverse_order(:number).limit(2, 1).from_self.reverse_order(:id).limit(1, 1)
217
+ ds.all.should == [{:number=>20, :id=>2}]
218
+ ds.columns.should == [:id, :number]
219
+ end
220
+
173
221
  specify "should alias columns correctly" do
174
222
  @ds.select(:id___x, :number___n).first.should == {:x=>1, :n=>10}
175
223
  end
@@ -274,6 +274,30 @@ describe "Database schema modifiers" do
274
274
  @db[:items2].all.should == [{:number=>10}]
275
275
  end
276
276
 
277
+ describe "views" do
278
+ before do
279
+ @db.drop_view(:items_view) rescue nil
280
+ @db.create_table(:items){Integer :number}
281
+ @ds.insert(:number=>1)
282
+ @ds.insert(:number=>2)
283
+ end
284
+ after do
285
+ @db.drop_view(:items_view)
286
+ end
287
+
288
+ specify "should create views correctly" do
289
+ @db.create_view(:items_view, @ds.where(:number=>1))
290
+ @db[:items_view].map(:number).should == [1]
291
+ end
292
+
293
+ specify "should create or replace views correctly" do
294
+ @db.create_or_replace_view(:items_view, @ds.where(:number=>1))
295
+ @db[:items_view].map(:number).should == [1]
296
+ @db.create_or_replace_view(:items_view, @ds.where(:number=>2))
297
+ @db[:items_view].map(:number).should == [2]
298
+ end
299
+ end
300
+
277
301
  specify "should handle create table in a rolled back transaction" do
278
302
  @db.drop_table?(:items)
279
303
  @db.transaction(:rollback=>:always){@db.create_table(:items){Integer :number}}
@@ -298,7 +322,7 @@ describe "Database schema modifiers" do
298
322
  specify "should create temporary tables without raising an exception" do
299
323
  @db.create_table!(:items, :temp=>true){Integer :number}
300
324
  end
301
-
325
+
302
326
  specify "should have create_table? only create the table if it doesn't already exist" do
303
327
  @db.create_table!(:items){String :a}
304
328
  @db.create_table?(:items){String :b}