sequel 3.43.0 → 3.44.0

Sign up to get free protection for your applications and to get access to all the features.
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}