sequel 3.21.0 → 3.24.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.
- data/CHANGELOG +112 -0
- data/README.rdoc +15 -1
- data/doc/association_basics.rdoc +159 -40
- data/doc/model_hooks.rdoc +64 -27
- data/doc/prepared_statements.rdoc +8 -4
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +2 -1
- data/lib/sequel/adapters/db2.rb +8 -1
- data/lib/sequel/adapters/firebird.rb +25 -9
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc.rb +39 -20
- data/lib/sequel/adapters/odbc.rb +2 -0
- data/lib/sequel/adapters/oracle.rb +12 -0
- data/lib/sequel/adapters/postgres.rb +30 -1
- data/lib/sequel/adapters/shared/access.rb +10 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +106 -11
- data/lib/sequel/adapters/shared/mysql.rb +25 -7
- data/lib/sequel/adapters/shared/postgres.rb +39 -6
- data/lib/sequel/adapters/shared/sqlite.rb +57 -5
- data/lib/sequel/adapters/sqlite.rb +8 -3
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +4 -3
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/core.rb +1 -1
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +6 -0
- data/lib/sequel/database/query.rb +33 -3
- data/lib/sequel/database/schema_methods.rb +13 -4
- data/lib/sequel/dataset/features.rb +6 -0
- data/lib/sequel/dataset/prepared_statements.rb +17 -2
- data/lib/sequel/dataset/query.rb +17 -0
- data/lib/sequel/dataset/sql.rb +2 -53
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +4 -3
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +234 -32
- data/lib/sequel/model/base.rb +187 -60
- data/lib/sequel/model/exceptions.rb +3 -1
- data/lib/sequel/model.rb +5 -0
- data/lib/sequel/plugins/association_pks.rb +6 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +2 -2
- data/lib/sequel/plugins/many_through_many.rb +33 -3
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +72 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +8 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +6 -0
- data/spec/adapters/postgres_spec.rb +43 -18
- data/spec/adapters/spec_helper.rb +5 -0
- data/spec/core/connection_pool_spec.rb +56 -77
- data/spec/core/database_spec.rb +33 -0
- data/spec/core/dataset_spec.rb +127 -16
- data/spec/core/expression_filters_spec.rb +13 -0
- data/spec/core/schema_spec.rb +13 -1
- data/spec/core/spec_helper.rb +5 -0
- data/spec/extensions/association_pks_spec.rb +7 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/many_through_many_spec.rb +77 -0
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +69 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +12 -0
- data/spec/integration/associations_test.rb +212 -0
- data/spec/integration/dataset_test.rb +8 -1
- data/spec/integration/plugin_test.rb +134 -0
- data/spec/integration/prepared_statement_test.rb +72 -1
- data/spec/integration/schema_test.rb +66 -8
- data/spec/integration/spec_helper.rb +5 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +7 -0
- data/spec/model/associations_spec.rb +463 -5
- data/spec/model/base_spec.rb +59 -0
- data/spec/model/eager_loading_spec.rb +269 -1
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/record_spec.rb +30 -0
- data/spec/model/spec_helper.rb +5 -0
- metadata +29 -4
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Dataset.introspect_all_columns" do
|
|
4
|
+
before do
|
|
5
|
+
@db = MODEL_DB
|
|
6
|
+
@ds = @db[:a]
|
|
7
|
+
class Sequel::Dataset
|
|
8
|
+
# Handle case where introspect_all_columns has already been called
|
|
9
|
+
alias columns columns_without_introspection unless instance_methods(false).map{|x| x.to_s}.include?('columns')
|
|
10
|
+
end
|
|
11
|
+
Sequel::Dataset.introspect_all_columns
|
|
12
|
+
@db.reset
|
|
13
|
+
end
|
|
14
|
+
after do
|
|
15
|
+
class Sequel::Dataset
|
|
16
|
+
alias columns columns_without_introspection
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
specify "should turn on column introspection by default" do
|
|
21
|
+
@ds.select(:x).columns.should == [:x]
|
|
22
|
+
@db.sqls.length.should == 0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "columns_introspection extension" do
|
|
27
|
+
before do
|
|
28
|
+
@db = MODEL_DB
|
|
29
|
+
@ds = @db[:a]
|
|
30
|
+
@ds.extend(Sequel::ColumnsIntrospection.dup) # dup to allow multiple places in class hierarchy
|
|
31
|
+
@db.reset
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
specify "should not issue a database query if the columns are already loaded" do
|
|
35
|
+
@ds.instance_variable_set(:@columns, [:x])
|
|
36
|
+
@ds.columns.should == [:x]
|
|
37
|
+
@db.sqls.length.should == 0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
specify "should handle plain symbols without a database query" do
|
|
41
|
+
@ds.select(:x).columns.should == [:x]
|
|
42
|
+
@db.sqls.length.should == 0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
specify "should handle qualified symbols without a database query" do
|
|
46
|
+
@ds.select(:t__x).columns.should == [:x]
|
|
47
|
+
@db.sqls.length.should == 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
specify "should handle aliased symbols without a database query" do
|
|
51
|
+
@ds.select(:x___a).columns.should == [:a]
|
|
52
|
+
@db.sqls.length.should == 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
specify "should handle qualified and aliased symbols without a database query" do
|
|
56
|
+
@ds.select(:t__x___a).columns.should == [:a]
|
|
57
|
+
@db.sqls.length.should == 0
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
specify "should handle SQL::Identifiers " do
|
|
61
|
+
@ds.select(:x.identifier).columns.should == [:x]
|
|
62
|
+
@db.sqls.length.should == 0
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
specify "should handle SQL::QualifiedIdentifiers" do
|
|
66
|
+
@ds.select(:x.qualify(:t)).columns.should == [:x]
|
|
67
|
+
@ds.select(:x.identifier.qualify(:t)).columns.should == [:x]
|
|
68
|
+
@db.sqls.length.should == 0
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
specify "should handle SQL::AliasedExpressions" do
|
|
72
|
+
@ds.select(:x.as(:a)).columns.should == [:a]
|
|
73
|
+
@ds.select(:x.as(:a.identifier)).columns.should == [:a]
|
|
74
|
+
@db.sqls.length.should == 0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
specify "should issue a database query if the wildcard is selected" do
|
|
78
|
+
@ds.columns
|
|
79
|
+
@db.sqls.length.should == 1
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
specify "should issue a database query if an unsupported type is used" do
|
|
83
|
+
@ds.select(1).columns
|
|
84
|
+
@db.sqls.length.should == 1
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
specify "should not have column introspection on by default" do
|
|
88
|
+
@db[:a].select(:x).columns
|
|
89
|
+
@db.sqls.length.should == 1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::DefaultsSetter" do
|
|
4
|
+
before do
|
|
5
|
+
@db = db = Sequel::Database.new
|
|
6
|
+
@c = c = Class.new(Sequel::Model(db[:foo]))
|
|
7
|
+
@c.instance_variable_set(:@db_schema, {:a=>{}})
|
|
8
|
+
@c.plugin :defaults_setter
|
|
9
|
+
@c.columns :a
|
|
10
|
+
@pr = proc{|x| db.meta_def(:schema){|*| [[:a, {:ruby_default => x}]]}; c.dataset = c.dataset; c}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should set default value upon initialization" do
|
|
14
|
+
@pr.call(2).new.values.should == {:a=>2}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should not mark the column as modified" do
|
|
18
|
+
@pr.call(2).new.changed_columns.should == []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should not set a default of nil" do
|
|
22
|
+
@pr.call(nil).new.values.should == {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should not override a given value" do
|
|
26
|
+
@pr.call(2)
|
|
27
|
+
@c.new('a'=>3).values.should == {:a=>3}
|
|
28
|
+
@c.new('a'=>nil).values.should == {:a=>nil}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should work correctly when subclassing" do
|
|
32
|
+
Class.new(@pr.call(2)).new.values.should == {:a=>2}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should contain the default values in default_values" do
|
|
36
|
+
@pr.call(2).default_values.should == {:a=>2}
|
|
37
|
+
@pr.call(nil).default_values.should == {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should allow modifications of default values" do
|
|
41
|
+
@pr.call(2)
|
|
42
|
+
@c.default_values[:a] = 3
|
|
43
|
+
@c.new.values.should == {:a => 3}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should allow proc default values" do
|
|
47
|
+
@pr.call(2)
|
|
48
|
+
@c.default_values[:a] = proc{3}
|
|
49
|
+
@c.new.values.should == {:a => 3}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should have procs that set default values set them to nil" do
|
|
53
|
+
@pr.call(2)
|
|
54
|
+
@c.default_values[:a] = proc{nil}
|
|
55
|
+
@c.new.values.should == {:a => nil}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should work correctly on a model without a dataset" do
|
|
59
|
+
@pr.call(2)
|
|
60
|
+
c = Class.new(Sequel::Model(@db[:bar]))
|
|
61
|
+
c.plugin :defaults_setter
|
|
62
|
+
c.default_values.should == {:a=>2}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -8,6 +8,7 @@ describe Sequel::Model, "many_through_many" do
|
|
|
8
8
|
plugin :many_through_many
|
|
9
9
|
end
|
|
10
10
|
class ::Tag < Sequel::Model
|
|
11
|
+
columns :id, :h1, :h2
|
|
11
12
|
end
|
|
12
13
|
MODEL_DB.reset
|
|
13
14
|
@c1 = Artist
|
|
@@ -111,6 +112,82 @@ describe Sequel::Model, "many_through_many" do
|
|
|
111
112
|
n.tags.should == [@c2.load(:id=>1)]
|
|
112
113
|
end
|
|
113
114
|
|
|
115
|
+
it "should allowing filtering by many_through_many associations" do
|
|
116
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
117
|
+
@c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should allowing filtering by many_through_many associations with a single through table" do
|
|
121
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id]]
|
|
122
|
+
@c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should allowing filtering by many_through_many associations with aliased tables" do
|
|
126
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :id, :id], [:albums_artists, :album_id, :tag_id]]
|
|
127
|
+
@c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.id = albums_artists.album_id) INNER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_artists_0.id) WHERE ((albums_artists_1.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL))))'
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "should allowing filtering by many_through_many associations with composite keys" do
|
|
131
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
132
|
+
@c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should allowing excluding by many_through_many associations" do
|
|
136
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
137
|
+
@c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should allowing excluding by many_through_many associations with composite keys" do
|
|
141
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
142
|
+
@c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "should allowing filtering by multiple many_through_many associations" do
|
|
146
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
147
|
+
@c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL))))'
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should allowing filtering by multiple many_through_many associations with composite keys" do
|
|
151
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
152
|
+
@c1.filter(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should allowing excluding by multiple many_through_many associations" do
|
|
156
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
157
|
+
@c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "should allowing excluding by multiple many_through_many associations with composite keys" do
|
|
161
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
162
|
+
@c1.exclude(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should allowing filtering/excluding many_through_many associations with NULL values" do
|
|
166
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
167
|
+
@c1.filter(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'f\''
|
|
168
|
+
@c1.exclude(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'t\''
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should allowing filtering by many_through_many association datasets" do
|
|
172
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
173
|
+
@c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL))))'
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "should allowing filtering by many_through_many association datasets with composite keys" do
|
|
177
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
178
|
+
@c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((id, yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT h1, h2 FROM tags WHERE ((x = 1) AND (h1 IS NOT NULL) AND (h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "should allowing excluding by many_through_many association datasets" do
|
|
182
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
|
183
|
+
@c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT id FROM tags WHERE ((x = 1) AND (id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (id IS NULL))'
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it "should allowing excluding by many_through_many association datasets with composite keys" do
|
|
187
|
+
@c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy]
|
|
188
|
+
@c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (((id, yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT h1, h2 FROM tags WHERE ((x = 1) AND (h1 IS NOT NULL) AND (h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (id IS NULL) OR (yyy IS NULL))'
|
|
189
|
+
end
|
|
190
|
+
|
|
114
191
|
it "should support a :conditions option" do
|
|
115
192
|
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
|
|
116
193
|
n = @c1.load(:id => 1234)
|
|
@@ -202,7 +202,7 @@ describe "Sequel::IntegerMigrator" do
|
|
|
202
202
|
@drops = []
|
|
203
203
|
@tables_created = []
|
|
204
204
|
@columns_created = []
|
|
205
|
-
@versions = {}
|
|
205
|
+
@versions = Hash.new{|h,k| h[k.to_sym]}
|
|
206
206
|
end
|
|
207
207
|
|
|
208
208
|
def version; versions.values.first || 0; end
|
|
@@ -212,7 +212,7 @@ describe "Sequel::IntegerMigrator" do
|
|
|
212
212
|
def create_table(name, opts={}, &block)
|
|
213
213
|
super
|
|
214
214
|
@columns_created << / \(?(\w+) integer.*\)?\z/.match(sqls.last)[1].to_sym
|
|
215
|
-
@tables_created << name
|
|
215
|
+
@tables_created << name.to_sym
|
|
216
216
|
end
|
|
217
217
|
|
|
218
218
|
def dataset(opts={})
|
|
@@ -228,7 +228,7 @@ describe "Sequel::IntegerMigrator" do
|
|
|
228
228
|
end
|
|
229
229
|
|
|
230
230
|
def table_exists?(name)
|
|
231
|
-
@tables_created.include?(name)
|
|
231
|
+
@tables_created.include?(name.to_sym)
|
|
232
232
|
end
|
|
233
233
|
end
|
|
234
234
|
@db = dbc.new
|
|
@@ -250,7 +250,7 @@ describe "Sequel::IntegerMigrator" do
|
|
|
250
250
|
|
|
251
251
|
specify "should add a column name if it doesn't already exist in the schema_info table" do
|
|
252
252
|
@db.create_table(:schema_info){Integer :v}
|
|
253
|
-
@db.should_receive(:alter_table).with(
|
|
253
|
+
@db.should_receive(:alter_table).with('schema_info')
|
|
254
254
|
Sequel::Migrator.apply(@db, @dirname)
|
|
255
255
|
end
|
|
256
256
|
|
|
@@ -320,45 +320,45 @@ describe "Sequel::TimestampMigrator" do
|
|
|
320
320
|
@dsc = dsc = Class.new(MockDataset) do
|
|
321
321
|
def columns
|
|
322
322
|
case opts[:from].first
|
|
323
|
-
when :schema_info
|
|
323
|
+
when :schema_info, 'schema_info'
|
|
324
324
|
[:version]
|
|
325
|
-
when :schema_migrations
|
|
325
|
+
when :schema_migrations, 'schema_migrations'
|
|
326
326
|
[:filename]
|
|
327
|
-
when :sm
|
|
327
|
+
when :sm, 'sm'
|
|
328
328
|
[:fn]
|
|
329
329
|
end
|
|
330
330
|
end
|
|
331
331
|
|
|
332
332
|
def fetch_rows(sql)
|
|
333
333
|
case opts[:from].first
|
|
334
|
-
when :schema_info
|
|
334
|
+
when :schema_info, 'schema_info'
|
|
335
335
|
yield({:version=>$sequel_migration_version})
|
|
336
|
-
when :schema_migrations
|
|
336
|
+
when :schema_migrations, 'schema_migrations'
|
|
337
337
|
$sequel_migration_files.sort.each{|f| yield(:filename=>f)}
|
|
338
|
-
when :sm
|
|
338
|
+
when :sm, 'sm'
|
|
339
339
|
$sequel_migration_files.sort.each{|f| yield(:fn=>f)}
|
|
340
340
|
end
|
|
341
341
|
end
|
|
342
342
|
|
|
343
343
|
def insert(h={})
|
|
344
344
|
case opts[:from].first
|
|
345
|
-
when :schema_info
|
|
345
|
+
when :schema_info, 'schema_info'
|
|
346
346
|
$sequel_migration_version = h.values.first
|
|
347
|
-
when :schema_migrations, :sm
|
|
347
|
+
when :schema_migrations, :sm, 'schema_migrations', 'sm'
|
|
348
348
|
$sequel_migration_files << h.values.first
|
|
349
349
|
end
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
def update(h={})
|
|
353
353
|
case opts[:from].first
|
|
354
|
-
when :schema_info
|
|
354
|
+
when :schema_info, 'schema_info'
|
|
355
355
|
$sequel_migration_version = h.values.first
|
|
356
356
|
end
|
|
357
357
|
end
|
|
358
358
|
|
|
359
359
|
def delete
|
|
360
360
|
case opts[:from].first
|
|
361
|
-
when :schema_migrations, :sm
|
|
361
|
+
when :schema_migrations, :sm, 'schema_migrations', 'sm'
|
|
362
362
|
$sequel_migration_files.delete(opts[:where].args.last)
|
|
363
363
|
end
|
|
364
364
|
end
|
|
@@ -366,9 +366,9 @@ describe "Sequel::TimestampMigrator" do
|
|
|
366
366
|
dbc = Class.new(MockDatabase) do
|
|
367
367
|
tables = {}
|
|
368
368
|
define_method(:dataset){|*a| dsc.new(self, *a)}
|
|
369
|
-
define_method(:create_table){|name, *args| tables[name] = true}
|
|
370
|
-
define_method(:drop_table){|*names| names.each{|n| tables.delete(n)}}
|
|
371
|
-
define_method(:table_exists?){|name| tables.has_key?(name)}
|
|
369
|
+
define_method(:create_table){|name, *args| tables[name.to_sym] = true}
|
|
370
|
+
define_method(:drop_table){|*names| names.each{|n| tables.delete(n.to_sym)}}
|
|
371
|
+
define_method(:table_exists?){|name| tables.has_key?(name.to_sym)}
|
|
372
372
|
end
|
|
373
373
|
@db = dbc.new
|
|
374
374
|
@m = Sequel::Migrator
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "Sequel::Plugins::AssociationPks" do
|
|
4
|
+
before do
|
|
5
|
+
@db = MODEL_DB.clone
|
|
6
|
+
mod = Module.new do
|
|
7
|
+
def fetch_rows(sql)
|
|
8
|
+
db << (is_a?(Sequel::Dataset::PreparedStatementMethods) ? :prepared : :regular)
|
|
9
|
+
db << sql
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
@db.meta_def(:dataset) do |*opts|
|
|
13
|
+
ds = super(*opts)
|
|
14
|
+
ds.extend mod
|
|
15
|
+
ds
|
|
16
|
+
end
|
|
17
|
+
def @db.transaction(opts)
|
|
18
|
+
execute('BEGIN')
|
|
19
|
+
yield
|
|
20
|
+
execute('COMMIT')
|
|
21
|
+
end
|
|
22
|
+
@Artist = Class.new(Sequel::Model(@db[:artists]))
|
|
23
|
+
@Artist.columns :id, :id2
|
|
24
|
+
@Album= Class.new(Sequel::Model(@db[:albums]))
|
|
25
|
+
@Album.columns :id, :artist_id, :id2, :artist_id2
|
|
26
|
+
@Tag = Class.new(Sequel::Model(@db[:tags]))
|
|
27
|
+
@Tag.columns :id, :id2
|
|
28
|
+
@Artist.plugin :prepared_statements_associations
|
|
29
|
+
@Album.plugin :prepared_statements_associations
|
|
30
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
|
31
|
+
@Album.many_to_one :artist, :class=>@Artist
|
|
32
|
+
@Album.many_to_many :tags, :class=>@Tag, :join_table=>:albums_tags, :left_key=>:album_id
|
|
33
|
+
@Artist.plugin :many_through_many
|
|
34
|
+
@Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :class=>@Tag
|
|
35
|
+
@db.reset
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
specify "should run correct SQL for associations" do
|
|
39
|
+
@Artist.load(:id=>1).albums
|
|
40
|
+
@db.sqls.should == [:prepared, "SELECT * FROM albums WHERE (albums.artist_id = 1)"]
|
|
41
|
+
@db.reset
|
|
42
|
+
|
|
43
|
+
@Album.load(:id=>1, :artist_id=>2).artist
|
|
44
|
+
@db.sqls.should == [:prepared, "SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1"]
|
|
45
|
+
@db.reset
|
|
46
|
+
|
|
47
|
+
@Album.load(:id=>1, :artist_id=>2).tags
|
|
48
|
+
@db.sqls.should == [:prepared, "SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.album_id = 1))"]
|
|
49
|
+
@db.reset
|
|
50
|
+
|
|
51
|
+
@Artist.load(:id=>1).tags
|
|
52
|
+
@db.sqls.should == [:prepared, "SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON ((albums.id = albums_tags.album_id) AND (albums.artist_id = 1))"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
specify "should run correct SQL for composite key associations" do
|
|
56
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>[:artist_id, :artist_id2], :primary_key=>[:id, :id2]
|
|
57
|
+
@Album.many_to_one :artist, :class=>@Artist, :key=>[:artist_id, :artist_id2], :primary_key=>[:id, :id2]
|
|
58
|
+
@Album.many_to_many :tags, :class=>@Tag, :join_table=>:albums_tags, :left_key=>[:album_id, :album_id2], :right_key=>[:tag_id, :tag_id2], :right_primary_key=>[:id, :id2], :left_primary_key=>[:id, :id2]
|
|
59
|
+
@Artist.many_through_many :tags, [[:albums, [:artist_id, :artist_id2], [:id, :id2]], [:albums_tags, [:album_id, :album_id2], [:tag_id, :tag_id2]]], :class=>@Tag, :right_primary_key=>[:id, :id2], :left_primary_key=>[:id, :id2]
|
|
60
|
+
|
|
61
|
+
@Artist.load(:id=>1, :id2=>2).albums
|
|
62
|
+
@db.sqls.should == [:prepared, "SELECT * FROM albums WHERE ((albums.artist_id = 1) AND (albums.artist_id2 = 2))"]
|
|
63
|
+
@db.reset
|
|
64
|
+
|
|
65
|
+
@Album.load(:id=>1, :artist_id=>2, :artist_id2=>3).artist
|
|
66
|
+
@db.sqls.should == [:prepared, "SELECT * FROM artists WHERE ((artists.id = 2) AND (artists.id2 = 3)) LIMIT 1"]
|
|
67
|
+
@db.reset
|
|
68
|
+
|
|
69
|
+
@Album.load(:id=>1, :artist_id=>2, :id2=>3).tags
|
|
70
|
+
@db.sqls.should == [:prepared, "SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.tag_id2 = tags.id2) AND (albums_tags.album_id = 1) AND (albums_tags.album_id2 = 3))"]
|
|
71
|
+
@db.reset
|
|
72
|
+
|
|
73
|
+
@Artist.load(:id=>1, :id2=>2).tags
|
|
74
|
+
@db.sqls.should == [:prepared, "SELECT tags.* FROM tags INNER JOIN albums_tags ON ((albums_tags.tag_id = tags.id) AND (albums_tags.tag_id2 = tags.id2)) INNER JOIN albums ON ((albums.id = albums_tags.album_id) AND (albums.id2 = albums_tags.album_id2) AND (albums.artist_id = 1) AND (albums.artist_id2 = 2))"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
specify "should not run query if no objects can be associated" do
|
|
78
|
+
@Artist.new.albums.should == []
|
|
79
|
+
@Album.new.artist.should == nil
|
|
80
|
+
@db.sqls.should == []
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
specify "should run a regular query if there is a callback" do
|
|
84
|
+
@Artist.load(:id=>1).albums(proc{|ds| ds})
|
|
85
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE (albums.artist_id = 1)"]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
specify "should run a regular query if :prepared_statement=>false option is used for the association" do
|
|
89
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :prepared_statement=>false
|
|
90
|
+
@Artist.load(:id=>1).albums
|
|
91
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE (albums.artist_id = 1)"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
specify "should run a regular query if unrecognized association is used" do
|
|
95
|
+
a = @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
|
|
96
|
+
a[:type] = :foo
|
|
97
|
+
@Artist.load(:id=>1).albums
|
|
98
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE (albums.artist_id = 1)"]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
specify "should run a regular query if a block is used when defining the association" do
|
|
102
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id do |ds| ds end
|
|
103
|
+
@Artist.load(:id=>1).albums
|
|
104
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE (albums.artist_id = 1)"]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
specify "should run a regular query if :conditions option is used when defining the association" do
|
|
108
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :conditions=>{:a=>1}
|
|
109
|
+
@Artist.load(:id=>1).albums
|
|
110
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE ((albums.artist_id = 1) AND (a = 1))"]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
specify "should run a regular query if :dataset option is used when defining the association" do
|
|
114
|
+
album = @Album
|
|
115
|
+
@Artist.one_to_many :albums, :class=>@Album, :dataset=>proc{album.filter(:artist_id=>id)}
|
|
116
|
+
@Artist.load(:id=>1).albums
|
|
117
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE (artist_id = 1)"]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
specify "should run a regular query if :cloning an association that doesn't used prepared statements" do
|
|
121
|
+
@Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id, :conditions=>{:a=>1}
|
|
122
|
+
@Artist.one_to_many :oalbums, :clone=>:albums
|
|
123
|
+
@Artist.load(:id=>1).oalbums
|
|
124
|
+
@db.sqls.should == [:regular, "SELECT * FROM albums WHERE ((albums.artist_id = 1) AND (a = 1))"]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "prepared_statements_safe plugin" do
|
|
4
|
+
before do
|
|
5
|
+
@c = Class.new(Sequel::Model(:people))
|
|
6
|
+
@c.columns :id, :name, :i
|
|
7
|
+
@ds = ds = @c.dataset
|
|
8
|
+
@c.instance_variable_set(:@db_schema, {:i=>{}, :name=>{}, :id=>{:primary_key=>true}})
|
|
9
|
+
def ds.fetch_rows(sql)
|
|
10
|
+
db << {:server=>@opts[:server] || :read_only}.merge(opts)[:server]
|
|
11
|
+
super{|h|}
|
|
12
|
+
yield(:id=>1, :name=>'foo', :i=>2)
|
|
13
|
+
end
|
|
14
|
+
def ds.update(*)
|
|
15
|
+
db << default_server_opts(opts)[:server]
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
def ds.insert(*)
|
|
19
|
+
db << default_server_opts(opts)[:server]
|
|
20
|
+
super
|
|
21
|
+
1
|
|
22
|
+
end
|
|
23
|
+
@c.plugin :prepared_statements_safe
|
|
24
|
+
@p = @c.load(:id=>1, :name=>'foo', :i=>2)
|
|
25
|
+
@c.db.execute 'foo'
|
|
26
|
+
@sqls = @c.db.sqls
|
|
27
|
+
@sqls.clear
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
specify "should load the prepared_statements plugin" do
|
|
31
|
+
@c.plugins.should include(Sequel::Plugins::PreparedStatements)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
specify "should set default values correctly" do
|
|
35
|
+
@c.prepared_statements_column_defaults.should == {:name=>nil, :i=>nil}
|
|
36
|
+
@c.instance_variable_set(:@db_schema, {:i=>{:default=>'f(x)'}, :name=>{:ruby_default=>'foo'}, :id=>{:primary_key=>true}})
|
|
37
|
+
Class.new(@c).prepared_statements_column_defaults.should == {:name=>'foo'}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
specify "should set default values when creating" do
|
|
41
|
+
@c.create
|
|
42
|
+
@sqls[1].should =~ /INSERT INTO people \((i|name), (i|name)\) VALUES \(NULL, NULL\)/
|
|
43
|
+
@sqls.clear
|
|
44
|
+
@c.create(:name=>'foo')
|
|
45
|
+
@sqls[1].should =~ /INSERT INTO people \((i|name), (i|name)\) VALUES \((NULL|'foo'), (NULL|'foo')\)/
|
|
46
|
+
@sqls.clear
|
|
47
|
+
@c.create(:name=>'foo', :i=>2)
|
|
48
|
+
@sqls[1].should =~ /INSERT INTO people \((i|name), (i|name)\) VALUES \((2|'foo'), (2|'foo')\)/
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
specify "should use database default values" do
|
|
52
|
+
@c.instance_variable_set(:@db_schema, {:i=>{:ruby_default=>2}, :name=>{:ruby_default=>'foo'}, :id=>{:primary_key=>true}})
|
|
53
|
+
c = Class.new(@c)
|
|
54
|
+
c.create
|
|
55
|
+
@sqls[1].should =~ /INSERT INTO people \((i|name), (i|name)\) VALUES \((2|'foo'), (2|'foo')\)/
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
specify "should not set defaults for unparseable dataset default values" do
|
|
59
|
+
@c.instance_variable_set(:@db_schema, {:i=>{:default=>'f(x)'}, :name=>{:ruby_default=>'foo'}, :id=>{:primary_key=>true}})
|
|
60
|
+
c = Class.new(@c)
|
|
61
|
+
c.create
|
|
62
|
+
@sqls[1].should == "INSERT INTO people (name) VALUES ('foo')"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
specify "should save all fields when updating" do
|
|
66
|
+
@p.update(:i=>3)
|
|
67
|
+
@sqls[1].should =~ /UPDATE people SET (name = 'foo'|i = 3), (name = 'foo'|i = 3) WHERE \(id = 1\)/
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "prepared_statements plugin" do
|
|
4
|
+
before do
|
|
5
|
+
@c = Class.new(Sequel::Model(:people))
|
|
6
|
+
@c.columns :id, :name, :i
|
|
7
|
+
@ds = ds = @c.dataset
|
|
8
|
+
def ds.fetch_rows(sql)
|
|
9
|
+
db << {:server=>@opts[:server] || :read_only}.merge(opts)[:server]
|
|
10
|
+
super{|h|}
|
|
11
|
+
yield(:id=>1, :name=>'foo', :i=>2)
|
|
12
|
+
end
|
|
13
|
+
def ds.update(*)
|
|
14
|
+
db << default_server_opts(opts)[:server]
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
def ds.insert(*)
|
|
18
|
+
db << default_server_opts(opts)[:server]
|
|
19
|
+
super
|
|
20
|
+
1
|
|
21
|
+
end
|
|
22
|
+
def ds.delete(*)
|
|
23
|
+
db << default_server_opts(opts)[:server]
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
@c.plugin :prepared_statements
|
|
27
|
+
@p = @c.load(:id=>1, :name=>'foo', :i=>2)
|
|
28
|
+
@c.db.execute 'foo'
|
|
29
|
+
@sqls = @c.db.sqls
|
|
30
|
+
@sqls.clear
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
specify "should correctly lookup by primary key" do
|
|
34
|
+
@c[1].should == @p
|
|
35
|
+
@sqls.should == [:read_only, "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
specify "should correctly delete instance" do
|
|
39
|
+
@p.destroy.should == @p
|
|
40
|
+
@sqls.should == [:default, "DELETE FROM people WHERE (id = 1)"]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
specify "should correctly update instance" do
|
|
44
|
+
@p.update(:name=>'bar').should == @c.load(:id=>1, :name=>'bar', :i => 2)
|
|
45
|
+
@sqls.should == [:default, "UPDATE people SET name = 'bar' WHERE (id = 1)"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
specify "should correctly create instance" do
|
|
49
|
+
@c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
|
|
50
|
+
@sqls.should == [:default, "INSERT INTO people (name) VALUES ('foo')", :default, "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
specify "should correctly create instance if dataset supports insert_select" do
|
|
54
|
+
def @ds.supports_insert_select?
|
|
55
|
+
true
|
|
56
|
+
end
|
|
57
|
+
def @ds.insert_select(h)
|
|
58
|
+
return {:id=>1, :name=>'foo', :i => 2}
|
|
59
|
+
end
|
|
60
|
+
def @ds.insert_sql(*)
|
|
61
|
+
"#{super}#{' RETURNING *' if opts.has_key?(:returning)}"
|
|
62
|
+
end
|
|
63
|
+
@c.create(:name=>'foo').should == @c.load(:id=>1, :name=>'foo', :i => 2)
|
|
64
|
+
@sqls.should == [:default, "INSERT INTO people (name) VALUES ('foo') RETURNING *"]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
specify "should work correctly when subclassing" do
|
|
68
|
+
c = Class.new(@c)
|
|
69
|
+
c[1].should == c.load(:id=>1, :name=>'foo', :i=>2)
|
|
70
|
+
@sqls.should == [:read_only, "SELECT * FROM people WHERE (id = 1) LIMIT 1"]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
|
|
2
|
+
|
|
3
|
+
describe "prepared_statements_with_pk plugin" do
|
|
4
|
+
before do
|
|
5
|
+
@c = Class.new(Sequel::Model(:people))
|
|
6
|
+
@c.columns :id, :name, :i
|
|
7
|
+
@ds = ds = @c.dataset
|
|
8
|
+
def ds.fetch_rows(sql)
|
|
9
|
+
db << {:server=>@opts[:server] || :read_only}.merge(opts)[:server]
|
|
10
|
+
super{|h|}
|
|
11
|
+
yield(:id=>1, :name=>'foo', :i=>2)
|
|
12
|
+
end
|
|
13
|
+
@c.plugin :prepared_statements_with_pk
|
|
14
|
+
@p = @c.load(:id=>1, :name=>'foo', :i=>2)
|
|
15
|
+
@c.db.execute 'foo'
|
|
16
|
+
@sqls = @c.db.sqls
|
|
17
|
+
@sqls.clear
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
specify "should load the prepared_statements plugin" do
|
|
21
|
+
@c.plugins.should include(Sequel::Plugins::PreparedStatements)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
specify "should correctly lookup by primary key from dataset" do
|
|
25
|
+
@c.dataset.filter(:name=>'foo')[1].should == @p
|
|
26
|
+
@sqls.should == [:read_only, "SELECT * FROM people WHERE ((name = 'foo') AND (id = 1)) LIMIT 1"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
specify "should still work correctly if there are multiple conflicting variables" do
|
|
30
|
+
@c.dataset.filter(:name=>'foo').or(:name=>'bar')[1].should == @p
|
|
31
|
+
@sqls.should == [:read_only, "SELECT * FROM people WHERE (((name = 'foo') OR (name = 'bar')) AND (id = 1)) LIMIT 1"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
specify "should still work correctly if the primary key is used elsewhere in the query" do
|
|
35
|
+
@c.dataset.filter{id > 2}[1].should == @p
|
|
36
|
+
@sqls.should == [:read_only, "SELECT * FROM people WHERE ((id > 2) AND (id = 1)) LIMIT 1"]
|
|
37
|
+
end
|
|
38
|
+
end
|