sequel 3.21.0 → 3.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +413 -3
- data/README.rdoc +20 -6
- data/Rakefile +23 -16
- data/bin/sequel +1 -5
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +86 -50
- data/doc/prepared_statements.rdoc +40 -13
- 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/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +115 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +15 -7
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +0 -5
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +5 -31
- data/lib/sequel/adapters/jdbc/db2.rb +44 -0
- data/lib/sequel/adapters/jdbc/derby.rb +217 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +26 -17
- data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
- data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
- data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +107 -38
- data/lib/sequel/adapters/mock.rb +315 -0
- data/lib/sequel/adapters/mysql.rb +78 -182
- data/lib/sequel/adapters/mysql2.rb +24 -23
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +0 -5
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +2 -4
- data/lib/sequel/adapters/oracle.rb +349 -51
- data/lib/sequel/adapters/postgres.rb +169 -21
- data/lib/sequel/adapters/shared/access.rb +21 -6
- data/lib/sequel/adapters/shared/db2.rb +288 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +108 -65
- data/lib/sequel/adapters/shared/mysql.rb +56 -13
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +185 -34
- data/lib/sequel/adapters/shared/postgres.rb +107 -58
- data/lib/sequel/adapters/shared/progress.rb +0 -6
- data/lib/sequel/adapters/shared/sqlite.rb +168 -41
- data/lib/sequel/adapters/sqlite.rb +27 -18
- data/lib/sequel/adapters/swift/mysql.rb +9 -5
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +6 -4
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +152 -17
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +46 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +100 -19
- data/lib/sequel/database/query.rb +153 -58
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +194 -57
- data/lib/sequel/dataset/features.rb +81 -8
- data/lib/sequel/dataset/graph.rb +8 -7
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +5 -6
- data/lib/sequel/dataset/prepared_statements.rb +32 -15
- data/lib/sequel/dataset/query.rb +223 -42
- data/lib/sequel/dataset/sql.rb +58 -61
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +997 -331
- data/lib/sequel/model/base.rb +386 -129
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +15 -8
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +65 -7
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +6 -1
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -25
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +44 -35
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +65 -29
- data/spec/adapters/mysql_spec.rb +122 -123
- data/spec/adapters/oracle_spec.rb +48 -76
- data/spec/adapters/postgres_spec.rb +213 -61
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +20 -31
- data/spec/core/database_spec.rb +729 -508
- data/spec/core/dataset_spec.rb +980 -1044
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +378 -0
- data/spec/core/object_graph_spec.rb +48 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/class_table_inheritance_spec.rb +1 -1
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/instance_hooks_spec.rb +71 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/named_timezones_spec.rb +22 -2
- data/spec/extensions/nested_attributes_spec.rb +4 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
- data/spec/extensions/serialization_spec.rb +5 -8
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +7 -1
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +522 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +375 -62
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +304 -116
- data/spec/integration/prepared_statement_test.rb +200 -120
- data/spec/integration/schema_test.rb +161 -30
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +172 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +174 -7
- data/spec/model/associations_spec.rb +849 -661
- data/spec/model/base_spec.rb +255 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +640 -676
- data/spec/model/hooks_spec.rb +309 -67
- data/spec/model/model_spec.rb +207 -167
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +321 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +95 -38
|
@@ -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)
|
|
@@ -481,24 +558,8 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
|
|
|
481
558
|
end
|
|
482
559
|
|
|
483
560
|
it "should cascade eagerly loading when the :eager_graph association option is used" do
|
|
484
|
-
Tag.dataset.extend(Module.new {
|
|
485
|
-
def columns
|
|
486
|
-
[:id]
|
|
487
|
-
end
|
|
488
|
-
def fetch_rows(sql)
|
|
489
|
-
@db << sql
|
|
490
|
-
yield({:id=>2, :tracks_id=>4, :x_foreign_key_x=>1})
|
|
491
|
-
end
|
|
492
|
-
})
|
|
493
561
|
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_graph=>:tracks
|
|
494
|
-
|
|
495
|
-
a.should == [@c1.load(:id=>1)]
|
|
496
|
-
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
497
|
-
'SELECT tags.id, tracks.id AS tracks_id, albums_artists.artist_id AS x_foreign_key_x FROM (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) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS tags LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums ON (albums.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)']
|
|
498
|
-
a = a.first
|
|
499
|
-
a.tags.should == [Tag.load(:id=>2)]
|
|
500
|
-
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
|
501
|
-
MODEL_DB.sqls.length.should == 2
|
|
562
|
+
proc{@c1.eager(:tags).all}.should raise_error(Sequel::Error)
|
|
502
563
|
end
|
|
503
564
|
|
|
504
565
|
it "should respect :eager_graph when lazily loading an association" do
|
|
@@ -560,6 +621,120 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
|
|
|
560
621
|
MODEL_DB.sqls.length.should == 2
|
|
561
622
|
end
|
|
562
623
|
|
|
624
|
+
it "should respect the :limit option on a many_through_many association" do
|
|
625
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2
|
|
626
|
+
Tag.dataset.extend(Module.new {
|
|
627
|
+
def fetch_rows(sql)
|
|
628
|
+
MODEL_DB.sqls << sql
|
|
629
|
+
yield({:x_foreign_key_x=>1, :id=>5})
|
|
630
|
+
yield({:x_foreign_key_x=>1, :id=>6})
|
|
631
|
+
yield({:x_foreign_key_x=>1, :id=>7})
|
|
632
|
+
end
|
|
633
|
+
})
|
|
634
|
+
a = @c1.eager(:first_two_tags).all
|
|
635
|
+
a.should == [@c1.load(:id=>1)]
|
|
636
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
637
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
|
638
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
639
|
+
MODEL_DB.sqls.length.should == 2
|
|
640
|
+
|
|
641
|
+
MODEL_DB.reset
|
|
642
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1]
|
|
643
|
+
a = @c1.eager(:first_two_tags).all
|
|
644
|
+
a.should == [@c1.load(:id=>1)]
|
|
645
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
646
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
|
647
|
+
a.first.first_two_tags.should == [Tag.load(:id=>6), Tag.load(:id=>7)]
|
|
648
|
+
MODEL_DB.sqls.length.should == 2
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
it "should respect the :limit option on a many_through_many association using a :window_function strategy" do
|
|
652
|
+
Tag.dataset.meta_def(:supports_window_functions?){true}
|
|
653
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>true, :order=>:name
|
|
654
|
+
Tag.dataset.extend(Module.new {
|
|
655
|
+
def fetch_rows(sql)
|
|
656
|
+
MODEL_DB.sqls << sql
|
|
657
|
+
yield({:x_foreign_key_x=>1, :id=>5})
|
|
658
|
+
yield({:x_foreign_key_x=>1, :id=>6})
|
|
659
|
+
end
|
|
660
|
+
})
|
|
661
|
+
a = @c1.eager(:first_two_tags).all
|
|
662
|
+
a.should == [@c1.load(:id=>1)]
|
|
663
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
664
|
+
'SELECT * FROM (SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x, row_number() OVER (PARTITION BY albums_artists.artist_id ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
|
|
665
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
666
|
+
MODEL_DB.sqls.length.should == 2
|
|
667
|
+
|
|
668
|
+
MODEL_DB.reset
|
|
669
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>true, :order=>:name
|
|
670
|
+
a = @c1.eager(:first_two_tags).all
|
|
671
|
+
a.should == [@c1.load(:id=>1)]
|
|
672
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
673
|
+
'SELECT * FROM (SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x, row_number() OVER (PARTITION BY albums_artists.artist_id ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
|
|
674
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
675
|
+
MODEL_DB.sqls.length.should == 2
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
it "should respect the :limit option on a many_through_many association with composite primary keys on the main table using a :window_function strategy" do
|
|
679
|
+
Tag.dataset.meta_def(:supports_window_functions?){true}
|
|
680
|
+
@c1.set_primary_key([:id1, :id2])
|
|
681
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, [:artist_id1, :artist_id2], :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>true, :order=>:name
|
|
682
|
+
@c1.dataset.extend(Module.new {
|
|
683
|
+
def fetch_rows(sql)
|
|
684
|
+
MODEL_DB.sqls << sql
|
|
685
|
+
yield({:id1=>1, :id2=>2})
|
|
686
|
+
end
|
|
687
|
+
})
|
|
688
|
+
Tag.dataset.extend(Module.new {
|
|
689
|
+
def fetch_rows(sql)
|
|
690
|
+
MODEL_DB.sqls << sql
|
|
691
|
+
yield({:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>2, :id=>5})
|
|
692
|
+
yield({:x_foreign_key_0_x=>1, :x_foreign_key_1_x=>2, :id=>6})
|
|
693
|
+
end
|
|
694
|
+
})
|
|
695
|
+
a = @c1.eager(:first_two_tags).all
|
|
696
|
+
a.should == [@c1.load(:id1=>1, :id2=>2)]
|
|
697
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
698
|
+
'SELECT * FROM (SELECT tags.*, albums_artists.artist_id1 AS x_foreign_key_0_x, albums_artists.artist_id2 AS x_foreign_key_1_x, row_number() OVER (PARTITION BY albums_artists.artist_id1, albums_artists.artist_id2 ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND ((albums_artists.artist_id1, albums_artists.artist_id2) IN ((1, 2))))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
|
|
699
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
700
|
+
MODEL_DB.sqls.length.should == 2
|
|
701
|
+
|
|
702
|
+
MODEL_DB.reset
|
|
703
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, [:artist_id1, :artist_id2], :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>true, :order=>:name
|
|
704
|
+
a = @c1.eager(:first_two_tags).all
|
|
705
|
+
a.should == [@c1.load(:id1=>1, :id2=>2)]
|
|
706
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
707
|
+
'SELECT * FROM (SELECT tags.*, albums_artists.artist_id1 AS x_foreign_key_0_x, albums_artists.artist_id2 AS x_foreign_key_1_x, row_number() OVER (PARTITION BY albums_artists.artist_id1, albums_artists.artist_id2 ORDER BY name) AS x_sequel_row_number_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND ((albums_artists.artist_id1, albums_artists.artist_id2) IN ((1, 2))))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
|
|
708
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
709
|
+
MODEL_DB.sqls.length.should == 2
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
it "should respect the :limit option on a many_through_many association using a :correlated_subquery strategy" do
|
|
713
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>2, :eager_limit_strategy=>:correlated_subquery, :order=>:name
|
|
714
|
+
Tag.dataset.extend(Module.new {
|
|
715
|
+
def fetch_rows(sql)
|
|
716
|
+
MODEL_DB.sqls << sql
|
|
717
|
+
yield({:x_foreign_key_x=>1, :id=>5})
|
|
718
|
+
yield({:x_foreign_key_x=>1, :id=>6})
|
|
719
|
+
end
|
|
720
|
+
})
|
|
721
|
+
a = @c1.eager(:first_two_tags).all
|
|
722
|
+
a.should == [@c1.load(:id=>1)]
|
|
723
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
724
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (tags.id IN (SELECT t1.id FROM tags AS t1 INNER JOIN albums_tags ON (albums_tags.tag_id = t1.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists AS t2 ON ((t2.album_id = albums.id) AND (t2.artist_id = albums_artists.artist_id)) ORDER BY name LIMIT 2)) ORDER BY name']
|
|
725
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
726
|
+
MODEL_DB.sqls.length.should == 2
|
|
727
|
+
|
|
728
|
+
MODEL_DB.reset
|
|
729
|
+
@c1.many_through_many :first_two_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag, :limit=>[2,1], :eager_limit_strategy=>:correlated_subquery, :order=>:name
|
|
730
|
+
a = @c1.eager(:first_two_tags).all
|
|
731
|
+
a.should == [@c1.load(:id=>1)]
|
|
732
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
|
733
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (tags.id IN (SELECT t1.id FROM tags AS t1 INNER JOIN albums_tags ON (albums_tags.tag_id = t1.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists AS t2 ON ((t2.album_id = albums.id) AND (t2.artist_id = albums_artists.artist_id)) ORDER BY name LIMIT 2 OFFSET 1)) ORDER BY name']
|
|
734
|
+
a.first.first_two_tags.should == [Tag.load(:id=>5), Tag.load(:id=>6)]
|
|
735
|
+
MODEL_DB.sqls.length.should == 2
|
|
736
|
+
end
|
|
737
|
+
|
|
563
738
|
it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
|
|
564
739
|
proc{@c1.eager(:tags).all}.should_not raise_error
|
|
565
740
|
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :allow_eager=>false
|
|
@@ -576,7 +751,7 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
|
|
|
576
751
|
MODEL_DB.sqls.length.should == 2
|
|
577
752
|
end
|
|
578
753
|
|
|
579
|
-
it "should respect
|
|
754
|
+
it "should respect many_through_many association's :left_primary_key and :right_primary_key options" do
|
|
580
755
|
@c1.send(:define_method, :yyy){values[:yyy]}
|
|
581
756
|
@c1.dataset.extend(Module.new {
|
|
582
757
|
def columns
|
|
@@ -705,8 +880,8 @@ describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
|
|
|
705
880
|
MODEL_DB.sqls.length.should == 1
|
|
706
881
|
end
|
|
707
882
|
|
|
708
|
-
it "eager graphing should give you a
|
|
709
|
-
@c1.eager_graph(:tags, :artists).first.should == {:
|
|
883
|
+
it "eager graphing should give you a plain hash when called without .all" do
|
|
884
|
+
@c1.eager_graph(:tags, :artists).first.should == {:albums_0_id=>3, :artists_0_id=>10, :id=>1, :tags_id=>2}
|
|
710
885
|
end
|
|
711
886
|
|
|
712
887
|
it "should be able to use eager and eager_graph together" do
|
|
@@ -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
|
|
@@ -66,8 +66,28 @@ describe "Sequel named_timezones extension" do
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
it "should work with the thread_local_timezones extension" do
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
q, q1, q2 = Queue.new, Queue.new, Queue.new
|
|
70
|
+
tz1, tz2 = nil, nil
|
|
71
|
+
t1 = Thread.new do
|
|
72
|
+
Sequel.thread_application_timezone = 'America/New_York'
|
|
73
|
+
q2.push nil
|
|
74
|
+
q.pop
|
|
75
|
+
tz1 = Sequel.application_timezone
|
|
76
|
+
end
|
|
77
|
+
t2 = Thread.new do
|
|
78
|
+
Sequel.thread_application_timezone = 'America/Los_Angeles'
|
|
79
|
+
q2.push nil
|
|
80
|
+
q1.pop
|
|
81
|
+
tz2 = Sequel.application_timezone
|
|
82
|
+
end
|
|
83
|
+
q2.pop
|
|
84
|
+
q2.pop
|
|
85
|
+
q.push nil
|
|
86
|
+
q1.push nil
|
|
87
|
+
t1.join
|
|
88
|
+
t2.join
|
|
89
|
+
tz1.should == @tz_out
|
|
90
|
+
tz2.should == @tz_in
|
|
71
91
|
end
|
|
72
92
|
end
|
|
73
93
|
end
|
|
@@ -13,6 +13,7 @@ describe "NestedAttributes plugin" do
|
|
|
13
13
|
mods << [:i, first_source, h, x]
|
|
14
14
|
x
|
|
15
15
|
end
|
|
16
|
+
define_method(:supports_insert_select?){true}
|
|
16
17
|
define_method(:insert_select) do |h|
|
|
17
18
|
x = ii.call
|
|
18
19
|
mods << [:is, first_source, h, x]
|
|
@@ -28,6 +29,9 @@ describe "NestedAttributes plugin" do
|
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
db = Sequel::Database.new({})
|
|
32
|
+
def db.connect(opts)
|
|
33
|
+
Object.new
|
|
34
|
+
end
|
|
31
35
|
db.meta_def(:dataset) do |*a|
|
|
32
36
|
x = super(*a)
|
|
33
37
|
x.extend(ds_mod)
|
|
@@ -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,79 @@
|
|
|
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
|
+
|
|
70
|
+
specify "should work with abstract classes" do
|
|
71
|
+
c = Class.new(Sequel::Model)
|
|
72
|
+
c.plugin :prepared_statements_safe
|
|
73
|
+
c1 = Class.new(c)
|
|
74
|
+
c1.meta_def(:get_db_schema){@db_schema = {:i=>{:default=>'f(x)'}, :name=>{:ruby_default=>'foo'}, :id=>{:primary_key=>true}}}
|
|
75
|
+
c1.set_dataset(:people)
|
|
76
|
+
c1.prepared_statements_column_defaults.should == {:name=>'foo'}
|
|
77
|
+
Class.new(c1).prepared_statements_column_defaults.should == {:name=>'foo'}
|
|
78
|
+
end
|
|
79
|
+
end
|