sequel 3.21.0 → 3.28.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 +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- 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 +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- 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/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- 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/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -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 +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -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 +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- 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/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- 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 +82 -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/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- 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 +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 +12 -20
- 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 +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
|
@@ -106,6 +106,9 @@ describe Sequel::Model, "#eager" do
|
|
|
106
106
|
end
|
|
107
107
|
})
|
|
108
108
|
end
|
|
109
|
+
after do
|
|
110
|
+
[:EagerAlbum, :EagerBand, :EagerTrack, :EagerGenre, :EagerBandMember].each{|x| Object.send(:remove_const, x)}
|
|
111
|
+
end
|
|
109
112
|
|
|
110
113
|
it "should raise an error if called without a symbol or hash" do
|
|
111
114
|
proc{EagerAlbum.eager(Object.new)}.should raise_error(Sequel::Error)
|
|
@@ -133,18 +136,68 @@ describe Sequel::Model, "#eager" do
|
|
|
133
136
|
MODEL_DB.sqls.length.should == 2
|
|
134
137
|
end
|
|
135
138
|
|
|
139
|
+
it "should eagerly load a single one_to_one association using the :distinct_on strategy" do
|
|
140
|
+
EagerTrack.dataset.meta_def(:supports_distinct_on?){true}
|
|
141
|
+
EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true
|
|
142
|
+
a = EagerAlbum.eager(:track).all
|
|
143
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
144
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT DISTINCT ON (tracks.album_id) * FROM tracks WHERE (tracks.album_id IN (1)) ORDER BY tracks.album_id']
|
|
145
|
+
a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
|
|
146
|
+
MODEL_DB.sqls.length.should == 2
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should eagerly load a single one_to_one association using the :window_function strategy" do
|
|
150
|
+
EagerTrack.dataset.meta_def(:supports_window_functions?){true}
|
|
151
|
+
EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name
|
|
152
|
+
a = EagerAlbum.eager(:track).all
|
|
153
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
154
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x = 1)']
|
|
155
|
+
a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
|
|
156
|
+
MODEL_DB.sqls.length.should == 2
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "should eagerly load a single one_to_one association using the :correlated_subquery strategy" do
|
|
160
|
+
EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name
|
|
161
|
+
a = EagerAlbum.eager(:track).all
|
|
162
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
163
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 1))) ORDER BY name']
|
|
164
|
+
a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
|
|
165
|
+
MODEL_DB.sqls.length.should == 2
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it "should handle qualified order clauses when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
|
|
169
|
+
EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>[:tracks__name, :tracks__name.desc, :name.qualify(:tracks), :name.qualify(:t), 1]
|
|
170
|
+
a = EagerAlbum.eager(:track).all
|
|
171
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
172
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY t1.name, t1.name DESC, t1.name, t.name, 1 LIMIT 1))) ORDER BY tracks.name, tracks.name DESC, tracks.name, t.name, 1']
|
|
173
|
+
a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
|
|
174
|
+
MODEL_DB.sqls.length.should == 2
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "should handle qualified composite keys when eagerly loading a single one_to_one association using the :correlated_subquery strategy" do
|
|
178
|
+
c1 = Class.new(EagerAlbum)
|
|
179
|
+
c2 = Class.new(EagerTrack)
|
|
180
|
+
c1.set_primary_key [:id, :band_id]
|
|
181
|
+
c2.set_primary_key [:id, :album_id]
|
|
182
|
+
c1.one_to_one :track, :class=>c2, :key=>[:album_id, :id], :eager_limit_strategy=>:correlated_subquery
|
|
183
|
+
c2.dataset.extend(Module.new do
|
|
184
|
+
def fetch_rows(sql)
|
|
185
|
+
MODEL_DB << sql
|
|
186
|
+
yield({:id => 2, :album_id=>1})
|
|
187
|
+
end
|
|
188
|
+
end)
|
|
189
|
+
a = c1.eager(:track).all
|
|
190
|
+
a.should == [c1.load(:id => 1, :band_id => 2)]
|
|
191
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (((tracks.album_id, tracks.id) IN ((1, 2))) AND ((tracks.id, tracks.album_id) IN (SELECT t1.id, t1.album_id FROM tracks AS t1 WHERE ((t1.album_id = tracks.album_id) AND (t1.id = tracks.id)) LIMIT 1)))']
|
|
192
|
+
a.first.track.should == c2.load(:id => 2, :album_id=>1)
|
|
193
|
+
MODEL_DB.sqls.length.should == 2
|
|
194
|
+
end
|
|
195
|
+
|
|
136
196
|
it "should eagerly load a single one_to_many association" do
|
|
137
197
|
a = EagerAlbum.eager(:tracks).all
|
|
138
|
-
a.should
|
|
139
|
-
a.size.should == 1
|
|
140
|
-
a.first.should be_a_kind_of(EagerAlbum)
|
|
141
|
-
a.first.values.should == {:id => 1, :band_id => 2}
|
|
198
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
142
199
|
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE (tracks.album_id IN (1))']
|
|
143
|
-
a
|
|
144
|
-
a.tracks.should be_a_kind_of(Array)
|
|
145
|
-
a.tracks.size.should == 1
|
|
146
|
-
a.tracks.first.should be_a_kind_of(EagerTrack)
|
|
147
|
-
a.tracks.first.values.should == {:id => 3, :album_id=>1}
|
|
200
|
+
a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
|
|
148
201
|
MODEL_DB.sqls.length.should == 2
|
|
149
202
|
end
|
|
150
203
|
|
|
@@ -163,6 +216,12 @@ describe Sequel::Model, "#eager" do
|
|
|
163
216
|
MODEL_DB.sqls.length.should == 2
|
|
164
217
|
end
|
|
165
218
|
|
|
219
|
+
it "should correctly handle a :select=>[] option to many_to_many" do
|
|
220
|
+
EagerAlbum.many_to_many :sgenres, :clone=>:genres, :select=>[]
|
|
221
|
+
a = EagerAlbum.eager(:sgenres).all
|
|
222
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT *, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
|
|
223
|
+
end
|
|
224
|
+
|
|
166
225
|
it "should eagerly load multiple associations in a single call" do
|
|
167
226
|
a = EagerAlbum.eager(:genres, :tracks, :band).all
|
|
168
227
|
a.should be_a_kind_of(Array)
|
|
@@ -309,25 +368,8 @@ describe Sequel::Model, "#eager" do
|
|
|
309
368
|
MODEL_DB.sqls.length.should == 2
|
|
310
369
|
end
|
|
311
370
|
|
|
312
|
-
it "should
|
|
313
|
-
|
|
314
|
-
def columns
|
|
315
|
-
[:id]
|
|
316
|
-
end
|
|
317
|
-
def fetch_rows(sql)
|
|
318
|
-
@db << sql
|
|
319
|
-
yield({:id=>5, :bands_id=>2, :p_k=>6, :x_foreign_key_x=>2})
|
|
320
|
-
yield({:id=>5, :bands_id=>3, :p_k=>6, :x_foreign_key_x=>2})
|
|
321
|
-
end
|
|
322
|
-
})
|
|
323
|
-
a = EagerBand.eager(:graph_members).all
|
|
324
|
-
a.should == [EagerBand.load(:id=>2)]
|
|
325
|
-
MODEL_DB.sqls.should == ['SELECT * FROM bands',
|
|
326
|
-
'SELECT members.id, bands.id AS bands_id, bands.p_k, bm.band_id AS x_foreign_key_x FROM (SELECT members.* FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))) AS members LEFT OUTER JOIN bm AS bm_0 ON (bm_0.member_id = members.id) LEFT OUTER JOIN bands ON (bands.id = bm_0.band_id) ORDER BY bands.id']
|
|
327
|
-
a = a.first
|
|
328
|
-
a.graph_members.should == [EagerBandMember.load(:id=>5)]
|
|
329
|
-
a.graph_members.first.bands.should == [EagerBand.load(:id=>2, :p_k=>6), EagerBand.load(:id=>3, :p_k=>6)]
|
|
330
|
-
MODEL_DB.sqls.length.should == 2
|
|
371
|
+
it "should raise an Error when eager loading a many_to_many association with the :eager_graph option" do
|
|
372
|
+
proc{EagerBand.eager(:graph_members).all}.should raise_error(Sequel::Error)
|
|
331
373
|
end
|
|
332
374
|
|
|
333
375
|
it "should respect :eager_graph when lazily loading an association" do
|
|
@@ -572,6 +614,135 @@ describe Sequel::Model, "#eager" do
|
|
|
572
614
|
as.first.special_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
573
615
|
end
|
|
574
616
|
|
|
617
|
+
it "should respect the :limit option on a one_to_many association" do
|
|
618
|
+
EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>2
|
|
619
|
+
EagerTrack.dataset.extend(Module.new {
|
|
620
|
+
def fetch_rows(sql)
|
|
621
|
+
MODEL_DB.sqls << sql
|
|
622
|
+
yield({:album_id=>1, :id=>2})
|
|
623
|
+
yield({:album_id=>1, :id=>3})
|
|
624
|
+
yield({:album_id=>1, :id=>4})
|
|
625
|
+
end
|
|
626
|
+
})
|
|
627
|
+
as = EagerAlbum.eager(:first_two_tracks).all
|
|
628
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
|
|
629
|
+
as.length.should == 1
|
|
630
|
+
as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>2), EagerTrack.load(:album_id=>1, :id=>3)]
|
|
631
|
+
|
|
632
|
+
MODEL_DB.reset
|
|
633
|
+
EagerAlbum.one_to_many :first_two_tracks, :class=>:EagerTrack, :key=>:album_id, :limit=>[2,1]
|
|
634
|
+
as = EagerAlbum.eager(:first_two_tracks).all
|
|
635
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM tracks WHERE (tracks.album_id IN (1))"]
|
|
636
|
+
as.length.should == 1
|
|
637
|
+
as.first.first_two_tracks.should == [EagerTrack.load(:album_id=>1, :id=>3), EagerTrack.load(:album_id=>1, :id=>4)]
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
it "should respect the :limit option on a one_to_many association using the :window_function strategy" do
|
|
641
|
+
EagerTrack.dataset.meta_def(:supports_window_functions?){true}
|
|
642
|
+
EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>2
|
|
643
|
+
a = EagerAlbum.eager(:tracks).all
|
|
644
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
645
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE (x_sequel_row_number_x <= 2)']
|
|
646
|
+
a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
|
|
647
|
+
MODEL_DB.sqls.length.should == 2
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
it "should respect the :limit option with an offset on a one_to_many association using the :window_function strategy" do
|
|
651
|
+
EagerTrack.dataset.meta_def(:supports_window_functions?){true}
|
|
652
|
+
EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>true, :order=>:name, :limit=>[2, 1]
|
|
653
|
+
a = EagerAlbum.eager(:tracks).all
|
|
654
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
655
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY tracks.album_id ORDER BY name) AS x_sequel_row_number_x FROM tracks WHERE (tracks.album_id IN (1))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))']
|
|
656
|
+
a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
|
|
657
|
+
MODEL_DB.sqls.length.should == 2
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
it "should respect the :limit option on a one_to_many association using the :correlated_subquery strategy" do
|
|
661
|
+
EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>2
|
|
662
|
+
a = EagerAlbum.eager(:tracks).all
|
|
663
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
664
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2))) ORDER BY name']
|
|
665
|
+
a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
|
|
666
|
+
MODEL_DB.sqls.length.should == 2
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
it "should respect the :limit option with an offset on a one_to_many association using the :correlated_subquery strategy" do
|
|
670
|
+
EagerAlbum.one_to_many :tracks, :class=>'EagerTrack', :key=>:album_id, :eager_limit_strategy=>:correlated_subquery, :order=>:name, :limit=>[2, 1]
|
|
671
|
+
a = EagerAlbum.eager(:tracks).all
|
|
672
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
673
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT * FROM tracks WHERE ((tracks.album_id IN (1)) AND (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) ORDER BY name LIMIT 2 OFFSET 1))) ORDER BY name']
|
|
674
|
+
a.first.tracks.should == [EagerTrack.load(:id => 3, :album_id=>1)]
|
|
675
|
+
MODEL_DB.sqls.length.should == 2
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
it "should respect the limit option on a many_to_many association" do
|
|
679
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
|
|
680
|
+
EagerGenre.dataset.extend(Module.new {
|
|
681
|
+
def fetch_rows(sql)
|
|
682
|
+
MODEL_DB.sqls << sql
|
|
683
|
+
yield({:x_foreign_key_x=>2, :id=>5})
|
|
684
|
+
yield({:x_foreign_key_x=>2, :id=>6})
|
|
685
|
+
yield({:x_foreign_key_x=>2, :id=>7})
|
|
686
|
+
end
|
|
687
|
+
})
|
|
688
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
689
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))"]
|
|
690
|
+
as.length.should == 1
|
|
691
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
692
|
+
|
|
693
|
+
MODEL_DB.reset
|
|
694
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>[2, 1]
|
|
695
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
696
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))"]
|
|
697
|
+
as.length.should == 1
|
|
698
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>6), EagerGenre.load(:id=>7)]
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
it "should respect the limit option on a many_to_many association using the :window_function strategy" do
|
|
702
|
+
EagerGenre.dataset.meta_def(:supports_window_functions?){true}
|
|
703
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>2, :order=>:name
|
|
704
|
+
EagerGenre.dataset.extend(Module.new {
|
|
705
|
+
def fetch_rows(sql)
|
|
706
|
+
MODEL_DB.sqls << sql
|
|
707
|
+
yield({:x_foreign_key_x=>2, :id=>5})
|
|
708
|
+
yield({:x_foreign_key_x=>2, :id=>6})
|
|
709
|
+
end
|
|
710
|
+
})
|
|
711
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
712
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE (x_sequel_row_number_x <= 2)"]
|
|
713
|
+
as.length.should == 1
|
|
714
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
715
|
+
|
|
716
|
+
MODEL_DB.reset
|
|
717
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>true, :limit=>[2, 1], :order=>:name
|
|
718
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
719
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT * FROM (SELECT genres.*, ag.album_id AS x_foreign_key_x, row_number() OVER (PARTITION BY ag.album_id ORDER BY name) AS x_sequel_row_number_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2)))) AS t1 WHERE ((x_sequel_row_number_x >= 2) AND (x_sequel_row_number_x < 4))"]
|
|
720
|
+
as.length.should == 1
|
|
721
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
it "should respect the limit option on a many_to_many association using the :correlated_subquery strategy" do
|
|
725
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>2, :order=>:name
|
|
726
|
+
EagerGenre.dataset.extend(Module.new {
|
|
727
|
+
def fetch_rows(sql)
|
|
728
|
+
MODEL_DB.sqls << sql
|
|
729
|
+
yield({:x_foreign_key_x=>2, :id=>5})
|
|
730
|
+
yield({:x_foreign_key_x=>2, :id=>6})
|
|
731
|
+
end
|
|
732
|
+
})
|
|
733
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
734
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2)) ORDER BY name"]
|
|
735
|
+
as.length.should == 1
|
|
736
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
737
|
+
|
|
738
|
+
MODEL_DB.reset
|
|
739
|
+
EagerAlbum.many_to_many :first_two_genres, :class=>:EagerGenre, :left_primary_key=>:band_id, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :eager_limit_strategy=>:correlated_subquery, :limit=>[2, 1], :order=>:name
|
|
740
|
+
as = EagerAlbum.eager(:first_two_genres).all
|
|
741
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT genres.*, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (2))) WHERE (genres.id IN (SELECT t1.id FROM genres AS t1 INNER JOIN ag AS t2 ON ((t2.genre_id = t1.id) AND (t2.album_id = ag.album_id)) ORDER BY name LIMIT 2 OFFSET 1)) ORDER BY name"]
|
|
742
|
+
as.length.should == 1
|
|
743
|
+
as.first.first_two_genres.should == [EagerGenre.load(:id=>5), EagerGenre.load(:id=>6)]
|
|
744
|
+
end
|
|
745
|
+
|
|
575
746
|
it "should use the :eager_loader association option when eager loading" do
|
|
576
747
|
EagerAlbum.many_to_one :special_band, :eager_loader=>(proc do |key_hash, records, assocs|
|
|
577
748
|
item = EagerBand.filter(:album_id=>records.collect{|r| [r.pk, r.pk*2]}.flatten).order(:name).first
|
|
@@ -646,6 +817,148 @@ describe Sequel::Model, "#eager" do
|
|
|
646
817
|
a.should == EagerAlbum.load(:id => 1, :band_id => 2)
|
|
647
818
|
a.al_genres.should == [EagerGenre.load(:id=>4)]
|
|
648
819
|
end
|
|
820
|
+
|
|
821
|
+
it "should eagerly load a many_to_one association with custom eager block" do
|
|
822
|
+
a = EagerAlbum.eager(:band => proc {|ds| ds.select(:id, :name)}).all
|
|
823
|
+
a.should be_a_kind_of(Array)
|
|
824
|
+
a.size.should == 1
|
|
825
|
+
a.first.should be_a_kind_of(EagerAlbum)
|
|
826
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
827
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id, name FROM bands WHERE (bands.id IN (2))']
|
|
828
|
+
a = a.first
|
|
829
|
+
a.band.should be_a_kind_of(EagerBand)
|
|
830
|
+
a.band.values.should == {:id => 2}
|
|
831
|
+
MODEL_DB.sqls.length.should == 2
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
it "should eagerly load a one_to_one association with custom eager block" do
|
|
835
|
+
EagerAlbum.one_to_one :track, :class=>'EagerTrack', :key=>:album_id
|
|
836
|
+
a = EagerAlbum.eager(:track => proc {|ds| ds.select(:id)}).all
|
|
837
|
+
a.should == [EagerAlbum.load(:id => 1, :band_id => 2)]
|
|
838
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
|
|
839
|
+
a.first.track.should == EagerTrack.load(:id => 3, :album_id=>1)
|
|
840
|
+
MODEL_DB.sqls.length.should == 2
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
it "should eagerly load a one_to_many association with custom eager block" do
|
|
844
|
+
a = EagerAlbum.eager(:tracks => proc {|ds| ds.select(:id)}).all
|
|
845
|
+
a.should be_a_kind_of(Array)
|
|
846
|
+
a.size.should == 1
|
|
847
|
+
a.first.should be_a_kind_of(EagerAlbum)
|
|
848
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
849
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', 'SELECT id FROM tracks WHERE (tracks.album_id IN (1))']
|
|
850
|
+
a = a.first
|
|
851
|
+
a.tracks.should be_a_kind_of(Array)
|
|
852
|
+
a.tracks.size.should == 1
|
|
853
|
+
a.tracks.first.should be_a_kind_of(EagerTrack)
|
|
854
|
+
a.tracks.first.values.should == {:id => 3, :album_id=>1}
|
|
855
|
+
MODEL_DB.sqls.length.should == 2
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
it "should eagerly load a many_to_many association with custom eager block" do
|
|
859
|
+
a = EagerAlbum.eager(:genres => proc {|ds| ds.select(:name)}).all
|
|
860
|
+
a.should be_a_kind_of(Array)
|
|
861
|
+
a.size.should == 1
|
|
862
|
+
a.first.should be_a_kind_of(EagerAlbum)
|
|
863
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
864
|
+
MODEL_DB.sqls.should == ['SELECT * FROM albums', "SELECT name, ag.album_id AS x_foreign_key_x FROM genres INNER JOIN ag ON ((ag.genre_id = genres.id) AND (ag.album_id IN (1)))"]
|
|
865
|
+
a = a.first
|
|
866
|
+
a.genres.should be_a_kind_of(Array)
|
|
867
|
+
a.genres.size.should == 1
|
|
868
|
+
a.genres.first.should be_a_kind_of(EagerGenre)
|
|
869
|
+
a.genres.first.values.should == {:id => 4}
|
|
870
|
+
MODEL_DB.sqls.length.should == 2
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
it "should allow cascading of eager loading within a custom eager block" do
|
|
874
|
+
a = EagerTrack.eager(:album => proc {|ds| ds.eager(:band => :members)}).all
|
|
875
|
+
a.should be_a_kind_of(Array)
|
|
876
|
+
a.size.should == 1
|
|
877
|
+
a.first.should be_a_kind_of(EagerTrack)
|
|
878
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
879
|
+
MODEL_DB.sqls.length.should == 4
|
|
880
|
+
MODEL_DB.sqls.should == ['SELECT * FROM tracks',
|
|
881
|
+
'SELECT * FROM albums WHERE (albums.id IN (1))',
|
|
882
|
+
'SELECT * FROM bands WHERE (bands.id IN (2))',
|
|
883
|
+
"SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
|
|
884
|
+
a = a.first
|
|
885
|
+
a.album.should be_a_kind_of(EagerAlbum)
|
|
886
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
887
|
+
a.album.band.should be_a_kind_of(EagerBand)
|
|
888
|
+
a.album.band.values.should == {:id => 2}
|
|
889
|
+
a.album.band.members.should be_a_kind_of(Array)
|
|
890
|
+
a.album.band.members.size.should == 1
|
|
891
|
+
a.album.band.members.first.should be_a_kind_of(EagerBandMember)
|
|
892
|
+
a.album.band.members.first.values.should == {:id => 5}
|
|
893
|
+
MODEL_DB.sqls.length.should == 4
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
it "should allow cascading of eager loading with custom callback with hash value" do
|
|
897
|
+
a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>{:band => :members}}).all
|
|
898
|
+
a.should be_a_kind_of(Array)
|
|
899
|
+
a.size.should == 1
|
|
900
|
+
a.first.should be_a_kind_of(EagerTrack)
|
|
901
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
902
|
+
MODEL_DB.sqls.length.should == 4
|
|
903
|
+
MODEL_DB.sqls.should == ['SELECT * FROM tracks',
|
|
904
|
+
'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
|
|
905
|
+
'SELECT * FROM bands WHERE (bands.id IN (2))',
|
|
906
|
+
"SELECT members.*, bm.band_id AS x_foreign_key_x FROM members INNER JOIN bm ON ((bm.member_id = members.id) AND (bm.band_id IN (2)))"]
|
|
907
|
+
a = a.first
|
|
908
|
+
a.album.should be_a_kind_of(EagerAlbum)
|
|
909
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
910
|
+
a.album.band.should be_a_kind_of(EagerBand)
|
|
911
|
+
a.album.band.values.should == {:id => 2}
|
|
912
|
+
a.album.band.members.should be_a_kind_of(Array)
|
|
913
|
+
a.album.band.members.size.should == 1
|
|
914
|
+
a.album.band.members.first.should be_a_kind_of(EagerBandMember)
|
|
915
|
+
a.album.band.members.first.values.should == {:id => 5}
|
|
916
|
+
MODEL_DB.sqls.length.should == 4
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
it "should allow cascading of eager loading with custom callback with symbol value" do
|
|
920
|
+
a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>:band}).all
|
|
921
|
+
a.should be_a_kind_of(Array)
|
|
922
|
+
a.size.should == 1
|
|
923
|
+
a.first.should be_a_kind_of(EagerTrack)
|
|
924
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
925
|
+
MODEL_DB.sqls.length.should == 3
|
|
926
|
+
MODEL_DB.sqls.should == ['SELECT * FROM tracks',
|
|
927
|
+
'SELECT id, band_id FROM albums WHERE (albums.id IN (1))',
|
|
928
|
+
'SELECT * FROM bands WHERE (bands.id IN (2))']
|
|
929
|
+
a = a.first
|
|
930
|
+
a.album.should be_a_kind_of(EagerAlbum)
|
|
931
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
932
|
+
a.album.band.should be_a_kind_of(EagerBand)
|
|
933
|
+
a.album.band.values.should == {:id => 2}
|
|
934
|
+
MODEL_DB.sqls.length.should == 3
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
it "should allow cascading of eager loading with custom callback with array value" do
|
|
938
|
+
a = EagerTrack.eager(:album=>{proc{|ds| ds.select(:id, :band_id)}=>[:band, :band_name]}).all
|
|
939
|
+
a.should be_a_kind_of(Array)
|
|
940
|
+
a.size.should == 1
|
|
941
|
+
a.first.should be_a_kind_of(EagerTrack)
|
|
942
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
943
|
+
MODEL_DB.sqls.length.should == 4
|
|
944
|
+
MODEL_DB.sqls[0..1].should == ['SELECT * FROM tracks',
|
|
945
|
+
'SELECT id, band_id FROM albums WHERE (albums.id IN (1))']
|
|
946
|
+
MODEL_DB.sqls[2..-1].sort.should == ['SELECT * FROM bands WHERE (bands.id IN (2))',
|
|
947
|
+
'SELECT id, name FROM bands WHERE (bands.id IN (2))']
|
|
948
|
+
a = a.first
|
|
949
|
+
a.album.should be_a_kind_of(EagerAlbum)
|
|
950
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
951
|
+
a.album.band.should be_a_kind_of(EagerBand)
|
|
952
|
+
a.album.band.values.should == {:id => 2}
|
|
953
|
+
a.album.band_name.should be_a_kind_of(EagerBand)
|
|
954
|
+
a.album.band_name.values.should == {:id => 2}
|
|
955
|
+
MODEL_DB.sqls.length.should == 4
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
it "should call both association and custom eager blocks" do
|
|
959
|
+
EagerBand.eager(:good_albums => proc {|ds| ds.select(:name)}).all
|
|
960
|
+
MODEL_DB.sqls.should == ['SELECT * FROM bands', "SELECT name FROM albums WHERE ((albums.band_id IN (2)) AND (name = 'good'))"]
|
|
961
|
+
end
|
|
649
962
|
end
|
|
650
963
|
|
|
651
964
|
describe Sequel::Model, "#eager_graph" do
|
|
@@ -664,6 +977,11 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
664
977
|
c.instance_variable_set(:@columns, (@columns.dup if @columns))
|
|
665
978
|
c
|
|
666
979
|
end
|
|
980
|
+
def select_columns(*a)
|
|
981
|
+
ds = select(*a)
|
|
982
|
+
ds.instance_variable_set(:@columns, a)
|
|
983
|
+
ds
|
|
984
|
+
end
|
|
667
985
|
end
|
|
668
986
|
|
|
669
987
|
class ::GraphAlbum < Sequel::Model(:albums)
|
|
@@ -924,13 +1242,13 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
924
1242
|
a.members.first.values.should == {:id => 5}
|
|
925
1243
|
end
|
|
926
1244
|
|
|
927
|
-
it "should give you a
|
|
1245
|
+
it "should give you a plain hash when called without .all" do
|
|
928
1246
|
ds = GraphAlbum.eager_graph(:band)
|
|
929
1247
|
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0, band.vocalist_id FROM albums LEFT OUTER JOIN bands AS band ON (band.id = albums.band_id)'
|
|
930
1248
|
def ds.fetch_rows(sql, &block)
|
|
931
1249
|
yield({:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3})
|
|
932
1250
|
end
|
|
933
|
-
ds.first.should == {:
|
|
1251
|
+
ds.first.should == {:id=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>3}
|
|
934
1252
|
end
|
|
935
1253
|
|
|
936
1254
|
it "should not drop any associated objects if the graph could not be a cartesian product" do
|
|
@@ -1201,6 +1519,27 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
1201
1519
|
as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
|
|
1202
1520
|
end
|
|
1203
1521
|
|
|
1522
|
+
it "should respect composite primary keys for classes when eager loading" do
|
|
1523
|
+
c1 = Class.new(GraphAlbum)
|
|
1524
|
+
c2 = Class.new(GraphBand)
|
|
1525
|
+
c1.set_primary_key [:band_id, :id]
|
|
1526
|
+
c2.set_primary_key [:vocalist_id, :id]
|
|
1527
|
+
c1.many_to_many :sbands, :class=>c2, :left_key=>[:l1, :l2], :right_key=>[:r1, :r2], :join_table=>:b
|
|
1528
|
+
c2.one_to_many :salbums, :class=>c1, :key=>[:band_id, :id]
|
|
1529
|
+
ds = c1.eager_graph(:sbands=>:salbums)
|
|
1530
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, sbands.id AS sbands_id, sbands.vocalist_id, salbums.id AS salbums_id, salbums.band_id AS salbums_band_id FROM albums LEFT OUTER JOIN b ON ((b.l1 = albums.band_id) AND (b.l2 = albums.id)) LEFT OUTER JOIN bands AS sbands ON ((sbands.vocalist_id = b.r1) AND (sbands.id = b.r2)) LEFT OUTER JOIN albums AS salbums ON ((salbums.band_id = sbands.vocalist_id) AND (salbums.id = sbands.id))'
|
|
1531
|
+
def ds.fetch_rows(sql, &block)
|
|
1532
|
+
yield({:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>7, :salbums_band_id=>8})
|
|
1533
|
+
yield({:id=>3, :band_id=>2, :sbands_id=>5, :vocalist_id=>6, :salbums_id=>9, :salbums_band_id=>10})
|
|
1534
|
+
yield({:id=>3, :band_id=>2, :sbands_id=>6, :vocalist_id=>22, :salbums_id=>nil, :salbums_band_id=>nil})
|
|
1535
|
+
yield({:id=>7, :band_id=>8, :sbands_id=>nil, :vocalist_id=>nil, :salbums_id=>nil, :salbums_band_id=>nil})
|
|
1536
|
+
end
|
|
1537
|
+
as = ds.all
|
|
1538
|
+
as.should == [c1.load(:id=>3, :band_id=>2), c1.load(:id=>7, :band_id=>8)]
|
|
1539
|
+
as.map{|x| x.sbands}.should == [[c2.load(:id=>5, :vocalist_id=>6), c2.load(:id=>6, :vocalist_id=>22)], []]
|
|
1540
|
+
as.map{|x| x.sbands.map{|y| y.salbums}}.should == [[[c1.load(:id=>7, :band_id=>8), c1.load(:id=>9, :band_id=>10)], []], []]
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1204
1543
|
it "should respect the association's :graph_select option" do
|
|
1205
1544
|
GraphAlbum.many_to_one :inner_band, :class=>'GraphBand', :key=>:band_id, :graph_select=>:vocalist_id
|
|
1206
1545
|
GraphAlbum.eager_graph(:inner_band).sql.should == 'SELECT albums.id, albums.band_id, inner_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS inner_band ON (inner_band.id = albums.band_id)'
|
|
@@ -1284,13 +1623,17 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
1284
1623
|
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :eager_grapher=>proc{|ds, aa, ta| ds.graph(GraphBand, {:active=>true}, :table_alias=>aa, :join_type=>:inner)}
|
|
1285
1624
|
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums INNER JOIN bands AS active_band ON (active_band.active IS TRUE)"
|
|
1286
1625
|
|
|
1287
|
-
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|
|
|
1626
|
+
GraphAlbum.one_to_many :right_tracks, :class=>'GraphTrack', :key=>:album_id, :eager_grapher=>proc{|eo| eo[:self].graph(GraphTrack, nil, :join_type=>:natural, :table_alias=>eo[:table_alias])}
|
|
1288
1627
|
GraphAlbum.eager_graph(:right_tracks).sql.should == 'SELECT albums.id, albums.band_id, right_tracks.id AS right_tracks_id, right_tracks.album_id FROM albums NATURAL JOIN tracks AS right_tracks'
|
|
1289
1628
|
|
|
1290
1629
|
GraphAlbum.many_to_many :active_genres, :class=>'GraphGenre', :eager_grapher=>proc{|ds, aa, ta| ds.graph(:ag, {:album_id=>:id}, :table_alias=>:a123, :implicit_qualifier=>ta).graph(GraphGenre, [:album_id], :table_alias=>aa)}
|
|
1291
1630
|
GraphAlbum.eager_graph(:active_genres).sql.should == "SELECT albums.id, albums.band_id, active_genres.id AS active_genres_id FROM albums LEFT OUTER JOIN ag AS a123 ON (a123.album_id = albums.id) LEFT OUTER JOIN genres AS active_genres USING (album_id)"
|
|
1292
1631
|
end
|
|
1293
1632
|
|
|
1633
|
+
it "should raise an error if you use an :eager_grapher proc with the wrong arity" do
|
|
1634
|
+
proc{GraphAlbum.many_to_one :special_band, :eager_grapher=>proc{|a, b|}}.should raise_error(Sequel::Error)
|
|
1635
|
+
end
|
|
1636
|
+
|
|
1294
1637
|
it "should respect the association's :graph_only_conditions option" do
|
|
1295
1638
|
GraphAlbum.many_to_one :active_band, :class=>'GraphBand', :key=>:band_id, :graph_only_conditions=>{:active=>true}
|
|
1296
1639
|
GraphAlbum.eager_graph(:active_band).sql.should == "SELECT albums.id, albums.band_id, active_band.id AS active_band_id, active_band.vocalist_id FROM albums LEFT OUTER JOIN bands AS active_band ON (active_band.active IS TRUE)"
|
|
@@ -1367,6 +1710,8 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
1367
1710
|
as = ds.all
|
|
1368
1711
|
as.should == [GraphAlbum.load(:id=>3, :band_id=>2)]
|
|
1369
1712
|
as.first.inner_genres.should == [GraphGenre.load(:id=>5), GraphGenre.load(:id=>6)]
|
|
1713
|
+
GraphAlbum.set_primary_key :id
|
|
1714
|
+
GraphGenre.set_primary_key :id
|
|
1370
1715
|
end
|
|
1371
1716
|
|
|
1372
1717
|
it "should handle eager loading with schemas and aliases of different types" do
|
|
@@ -1392,4 +1737,147 @@ describe Sequel::Model, "#eager_graph" do
|
|
|
1392
1737
|
ds = c1.join(:s__t, [:b_id]).eager_graph(:a_genres)
|
|
1393
1738
|
ds.sql.should == 'SELECT a.id, a_genres.id AS a_genres_id FROM (SELECT * FROM s.a INNER JOIN s.t USING (b_id)) AS a LEFT OUTER JOIN s.ag AS ag ON (ag.album_id = a.id) LEFT OUTER JOIN s.g AS a_genres ON (a_genres.id = ag.genre_id)'
|
|
1394
1739
|
end
|
|
1740
|
+
|
|
1741
|
+
it "should respect :after_load callbacks on associations when eager graphing" do
|
|
1742
|
+
GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id, :after_load=>proc{|o, a| a.id *=2}
|
|
1743
|
+
GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
|
|
1744
|
+
GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :after_load=>proc{|o, os| os.each{|a| a.id *=2}}
|
|
1745
|
+
ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
|
|
1746
|
+
ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_tracks.id AS al_tracks_id, al_tracks.album_id, al_genres.id AS al_genres_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genres ON (al_genres.id = ag.genre_id)"
|
|
1747
|
+
def ds.fetch_rows(sql)
|
|
1748
|
+
yield({:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_tracks_id=>5, :album_id=>6, :al_genres_id=>7})
|
|
1749
|
+
end
|
|
1750
|
+
a = ds.all.first
|
|
1751
|
+
a.should == GraphAlbum.load(:id => 1, :band_id => 2)
|
|
1752
|
+
a.al_band.should == GraphBand.load(:id=>6, :vocalist_id=>4)
|
|
1753
|
+
a.al_tracks.should == [GraphTrack.load(:id=>10, :album_id=>6)]
|
|
1754
|
+
a.al_genres.should == [GraphGenre.load(:id=>14)]
|
|
1755
|
+
end
|
|
1756
|
+
|
|
1757
|
+
it "should respect limits on associations when eager graphing" do
|
|
1758
|
+
GraphAlbum.many_to_one :al_band, :class=>GraphBand, :key=>:band_id
|
|
1759
|
+
GraphAlbum.one_to_many :al_tracks, :class=>GraphTrack, :key=>:album_id, :limit=>2
|
|
1760
|
+
GraphAlbum.many_to_many :al_genres, :class=>GraphGenre, :left_key=>:album_id, :right_key=>:genre_id, :join_table=>:ag, :limit=>2
|
|
1761
|
+
ds = GraphAlbum.eager_graph(:al_band, :al_tracks, :al_genres)
|
|
1762
|
+
ds.sql.should == "SELECT albums.id, albums.band_id, al_band.id AS al_band_id, al_band.vocalist_id, al_tracks.id AS al_tracks_id, al_tracks.album_id, al_genres.id AS al_genres_id FROM albums LEFT OUTER JOIN bands AS al_band ON (al_band.id = albums.band_id) LEFT OUTER JOIN tracks AS al_tracks ON (al_tracks.album_id = albums.id) LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN genres AS al_genres ON (al_genres.id = ag.genre_id)"
|
|
1763
|
+
def ds.fetch_rows(sql)
|
|
1764
|
+
yield({:id=>1, :band_id=>2, :al_band_id=>3, :vocalist_id=>4, :al_tracks_id=>5, :album_id=>6, :al_genres_id=>7})
|
|
1765
|
+
yield({:id=>1, :band_id=>2, :al_band_id=>8, :vocalist_id=>9, :al_tracks_id=>10, :album_id=>11, :al_genres_id=>12})
|
|
1766
|
+
yield({:id=>1, :band_id=>2, :al_band_id=>13, :vocalist_id=>14, :al_tracks_id=>15, :album_id=>16, :al_genres_id=>17})
|
|
1767
|
+
end
|
|
1768
|
+
a = ds.all.first
|
|
1769
|
+
a.should == GraphAlbum.load(:id => 1, :band_id => 2)
|
|
1770
|
+
a.al_band.should == GraphBand.load(:id=>3, :vocalist_id=>4)
|
|
1771
|
+
a.al_tracks.should == [GraphTrack.load(:id=>5, :album_id=>6), GraphTrack.load(:id=>10, :album_id=>11)]
|
|
1772
|
+
a.al_genres.should == [GraphGenre.load(:id=>7), GraphGenre.load(:id=>12)]
|
|
1773
|
+
end
|
|
1774
|
+
|
|
1775
|
+
it "should eagerly load a many_to_one association with a custom callback" do
|
|
1776
|
+
ds = GraphAlbum.eager_graph(:band => proc {|ds| ds.select_columns(:id)})
|
|
1777
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, band.id AS band_id_0 FROM albums LEFT OUTER JOIN (SELECT id FROM bands) AS band ON (band.id = albums.band_id)'
|
|
1778
|
+
def ds.fetch_rows(sql, &block)
|
|
1779
|
+
yield({:id=>1, :band_id=>2, :band_id_0=>2})
|
|
1780
|
+
end
|
|
1781
|
+
a = ds.all
|
|
1782
|
+
a.should be_a_kind_of(Array)
|
|
1783
|
+
a.size.should == 1
|
|
1784
|
+
a.first.should be_a_kind_of(GraphAlbum)
|
|
1785
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
1786
|
+
a = a.first
|
|
1787
|
+
a.band.should be_a_kind_of(GraphBand)
|
|
1788
|
+
a.band.values.should == {:id => 2}
|
|
1789
|
+
end
|
|
1790
|
+
|
|
1791
|
+
it "should eagerly load a one_to_one association with a custom callback" do
|
|
1792
|
+
GraphAlbum.one_to_one :track, :class=>'GraphTrack', :key=>:album_id
|
|
1793
|
+
ds = GraphAlbum.eager_graph(:track => proc {|ds| ds.select_columns(:album_id)})
|
|
1794
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, track.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS track ON (track.album_id = albums.id)'
|
|
1795
|
+
def ds.fetch_rows(sql, &block)
|
|
1796
|
+
yield({:id=>1, :band_id=>2, :album_id=>1})
|
|
1797
|
+
end
|
|
1798
|
+
a = ds.all
|
|
1799
|
+
a.should == [GraphAlbum.load(:id => 1, :band_id => 2)]
|
|
1800
|
+
a.first.track.should == GraphTrack.load(:album_id=>1)
|
|
1801
|
+
end
|
|
1802
|
+
|
|
1803
|
+
it "should eagerly load a one_to_many association with a custom callback" do
|
|
1804
|
+
ds = GraphAlbum.eager_graph(:tracks => proc {|ds| ds.select_columns(:album_id)})
|
|
1805
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, tracks.album_id FROM albums LEFT OUTER JOIN (SELECT album_id FROM tracks) AS tracks ON (tracks.album_id = albums.id)'
|
|
1806
|
+
def ds.fetch_rows(sql, &block)
|
|
1807
|
+
yield({:id=>1, :band_id=>2, :album_id=>1})
|
|
1808
|
+
end
|
|
1809
|
+
a = ds.all
|
|
1810
|
+
a.should be_a_kind_of(Array)
|
|
1811
|
+
a.size.should == 1
|
|
1812
|
+
a.first.should be_a_kind_of(GraphAlbum)
|
|
1813
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
1814
|
+
a = a.first
|
|
1815
|
+
a.tracks.should be_a_kind_of(Array)
|
|
1816
|
+
a.tracks.size.should == 1
|
|
1817
|
+
a.tracks.first.should be_a_kind_of(GraphTrack)
|
|
1818
|
+
a.tracks.first.values.should == {:album_id=>1}
|
|
1819
|
+
end
|
|
1820
|
+
|
|
1821
|
+
it "should eagerly load a many_to_many association with a custom callback" do
|
|
1822
|
+
ds = GraphAlbum.eager_graph(:genres => proc {|ds| ds.select_columns(:id)})
|
|
1823
|
+
ds.sql.should == 'SELECT albums.id, albums.band_id, genres.id AS genres_id FROM albums LEFT OUTER JOIN ag ON (ag.album_id = albums.id) LEFT OUTER JOIN (SELECT id FROM genres) AS genres ON (genres.id = ag.genre_id)'
|
|
1824
|
+
def ds.fetch_rows(sql, &block)
|
|
1825
|
+
yield({:id=>1, :band_id=>2, :genres_id=>4})
|
|
1826
|
+
end
|
|
1827
|
+
a = ds.all
|
|
1828
|
+
a.should be_a_kind_of(Array)
|
|
1829
|
+
a.size.should == 1
|
|
1830
|
+
a.first.should be_a_kind_of(GraphAlbum)
|
|
1831
|
+
a.first.values.should == {:id => 1, :band_id => 2}
|
|
1832
|
+
a = a.first
|
|
1833
|
+
a.genres.should be_a_kind_of(Array)
|
|
1834
|
+
a.genres.size.should == 1
|
|
1835
|
+
a.genres.first.should be_a_kind_of(GraphGenre)
|
|
1836
|
+
a.genres.first.values.should == {:id => 4}
|
|
1837
|
+
end
|
|
1838
|
+
|
|
1839
|
+
it "should allow cascading of eager loading with a custom callback with hash value" do
|
|
1840
|
+
ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select_columns(:id, :band_id)}=>{:band=>:members}})
|
|
1841
|
+
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, members.id AS members_id FROM tracks LEFT OUTER JOIN (SELECT id, band_id FROM albums) AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN bm ON (bm.band_id = band.id) LEFT OUTER JOIN members ON (members.id = bm.member_id)'
|
|
1842
|
+
def ds.fetch_rows(sql, &block)
|
|
1843
|
+
yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :members_id=>5, :band_id_0=>2, :vocalist_id=>6})
|
|
1844
|
+
end
|
|
1845
|
+
a = ds.all
|
|
1846
|
+
a.should be_a_kind_of(Array)
|
|
1847
|
+
a.size.should == 1
|
|
1848
|
+
a.first.should be_a_kind_of(GraphTrack)
|
|
1849
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
1850
|
+
a = a.first
|
|
1851
|
+
a.album.should be_a_kind_of(GraphAlbum)
|
|
1852
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
1853
|
+
a.album.band.should be_a_kind_of(GraphBand)
|
|
1854
|
+
a.album.band.values.should == {:id => 2, :vocalist_id=>6}
|
|
1855
|
+
a.album.band.members.should be_a_kind_of(Array)
|
|
1856
|
+
a.album.band.members.size.should == 1
|
|
1857
|
+
a.album.band.members.first.should be_a_kind_of(GraphBandMember)
|
|
1858
|
+
a.album.band.members.first.values.should == {:id => 5}
|
|
1859
|
+
end
|
|
1860
|
+
|
|
1861
|
+
it "should allow cascading of eager loading with a custom callback with array value" do
|
|
1862
|
+
ds = GraphTrack.eager_graph(:album=>{proc{|ds| ds.select_columns(:id, :band_id)}=>[:band, :tracks]})
|
|
1863
|
+
ds.sql.should == 'SELECT tracks.id, tracks.album_id, album.id AS album_id_0, album.band_id, band.id AS band_id_0, band.vocalist_id, tracks_0.id AS tracks_0_id, tracks_0.album_id AS tracks_0_album_id FROM tracks LEFT OUTER JOIN (SELECT id, band_id FROM albums) AS album ON (album.id = tracks.album_id) LEFT OUTER JOIN bands AS band ON (band.id = album.band_id) LEFT OUTER JOIN tracks AS tracks_0 ON (tracks_0.album_id = album.id)'
|
|
1864
|
+
def ds.fetch_rows(sql, &block)
|
|
1865
|
+
yield({:id=>3, :album_id=>1, :album_id_0=>1, :band_id=>2, :band_id_0=>2, :vocalist_id=>6, :tracks_0_id=>3, :tracks_0_album_id=>1})
|
|
1866
|
+
end
|
|
1867
|
+
a = ds.all
|
|
1868
|
+
a.should be_a_kind_of(Array)
|
|
1869
|
+
a.size.should == 1
|
|
1870
|
+
a.first.should be_a_kind_of(GraphTrack)
|
|
1871
|
+
a.first.values.should == {:id => 3, :album_id => 1}
|
|
1872
|
+
a = a.first
|
|
1873
|
+
a.album.should be_a_kind_of(GraphAlbum)
|
|
1874
|
+
a.album.values.should == {:id => 1, :band_id => 2}
|
|
1875
|
+
a.album.band.should be_a_kind_of(GraphBand)
|
|
1876
|
+
a.album.band.values.should == {:id => 2, :vocalist_id=>6}
|
|
1877
|
+
a.album.tracks.should be_a_kind_of(Array)
|
|
1878
|
+
a.album.tracks.size.should == 1
|
|
1879
|
+
a.album.tracks.first.should be_a_kind_of(GraphTrack)
|
|
1880
|
+
a.album.tracks.first.values.should == {:id => 3, :album_id => 1}
|
|
1881
|
+
end
|
|
1882
|
+
|
|
1395
1883
|
end
|