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.
Files changed (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. metadata +85 -34
@@ -5,6 +5,11 @@ unless Object.const_defined?('Sequel')
5
5
  require 'sequel/core'
6
6
  end
7
7
 
8
+ if ENV['SEQUEL_COLUMNS_INTROSPECTION']
9
+ Sequel.extension :columns_introspection
10
+ Sequel::Dataset.introspect_all_columns
11
+ end
12
+
8
13
  class MockDataset < Sequel::Dataset
9
14
  def insert(*args)
10
15
  @db.execute insert_sql(*args)
@@ -55,3 +60,43 @@ class SchemaDummyDatabase < Sequel::Database
55
60
  @sqls << sql
56
61
  end
57
62
  end
63
+
64
+ class DummyDataset < Sequel::Dataset
65
+ def first
66
+ raise if @opts[:from] == [:a]
67
+ true
68
+ end
69
+ end
70
+
71
+ class DummyDatabase < Sequel::Database
72
+ attr_reader :sqls
73
+
74
+ def execute(sql, opts={})
75
+ @sqls ||= []
76
+ @sqls << sql
77
+ end
78
+
79
+ def transaction; yield; end
80
+
81
+ def dataset
82
+ DummyDataset.new(self)
83
+ end
84
+ end
85
+
86
+ class Dummy2Database < Sequel::Database
87
+ attr_reader :sql
88
+ def execute(sql); @sql = sql; end
89
+ def transaction; yield; end
90
+ end
91
+
92
+ class DummyDataset < Sequel::Dataset
93
+ VALUES = [
94
+ {:a => 1, :b => 2},
95
+ {:a => 3, :b => 4},
96
+ {:a => 5, :b => 6}
97
+ ]
98
+ def fetch_rows(sql, &block)
99
+ VALUES.each(&block)
100
+ end
101
+ end
102
+
@@ -57,6 +57,13 @@ describe "Sequel::Plugins::AssociationPks" do
57
57
  "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN (1, 2)))"]
58
58
  end
59
59
 
60
+ specify "should use associated class's primary key for a one_to_many association" do
61
+ @Album.set_primary_key :foo
62
+ @Artist.load(:id=>1).album_pks = [1, 2]
63
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (foo IN (1, 2))",
64
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (foo NOT IN (1, 2)))"]
65
+ end
66
+
60
67
  specify "should set associated pks correctly for a many_to_many association" do
61
68
  @Album.load(:id=>2).tag_pks = [1, 3]
62
69
  @db.sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
@@ -82,4 +89,35 @@ describe "Sequel::Plugins::AssociationPks" do
82
89
  @db.sqls[3].should == "COMMIT"
83
90
  end
84
91
 
92
+ specify "should automatically convert keys to numbers if the primary key is an integer for one_to_many associations" do
93
+ @Album.db_schema[:id][:type] = :integer
94
+ @Artist.load(:id=>1).album_pks = %w'1 2'
95
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (id IN (1, 2))",
96
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN (1, 2)))"]
97
+ end
98
+
99
+ specify "should not automatically convert keys if the primary key is not an integer for many_to_many associations" do
100
+ @Album.db_schema[:id][:type] = :string
101
+ @Artist.load(:id=>1).album_pks = %w'1 2'
102
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (id IN ('1', '2'))",
103
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN ('1', '2')))"]
104
+ end
105
+
106
+ specify "should automatically convert keys to numbers if the primary key is an integer for one_to_many associations" do
107
+ @Tag.db_schema[:id][:type] = :integer
108
+ @Album.load(:id=>2).tag_pks = %w'1 3'
109
+ @db.sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
110
+ @db.sqls[1].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
111
+ @db.sqls.length.should == 2
112
+ end
113
+
114
+ specify "should not automatically convert keys to numbers if the primary key is an integer for many_to_many associations" do
115
+ @Tag.db_schema[:id][:type] = :string
116
+ @Album.load(:id=>2).tag_pks = %w'1 3'
117
+ @db.sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN ('1', '3')))"
118
+ @db.sqls[1].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '1'|'1', 2)\)/
119
+ @db.sqls[2].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '3'|'3', 2)\)/
120
+ @db.sqls.length.should == 3
121
+ end
122
+
85
123
  end
@@ -0,0 +1,91 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper')
2
+
3
+ describe "Sequel::Dataset.introspect_all_columns" do
4
+ before do
5
+ @db = MODEL_DB
6
+ @ds = @db[:a]
7
+ class Sequel::Dataset
8
+ # Handle case where introspect_all_columns has already been called
9
+ alias columns columns_without_introspection unless instance_methods(false).map{|x| x.to_s}.include?('columns')
10
+ end
11
+ Sequel::Dataset.introspect_all_columns
12
+ @db.reset
13
+ end
14
+ after do
15
+ class Sequel::Dataset
16
+ alias columns columns_without_introspection
17
+ end
18
+ end
19
+
20
+ specify "should turn on column introspection by default" do
21
+ @ds.select(:x).columns.should == [:x]
22
+ @db.sqls.length.should == 0
23
+ end
24
+ end
25
+
26
+ describe "columns_introspection extension" do
27
+ before do
28
+ @db = MODEL_DB
29
+ @ds = @db[:a]
30
+ @ds.extend(Sequel::ColumnsIntrospection.dup) # dup to allow multiple places in class hierarchy
31
+ @db.reset
32
+ end
33
+
34
+ specify "should not issue a database query if the columns are already loaded" do
35
+ @ds.instance_variable_set(:@columns, [:x])
36
+ @ds.columns.should == [:x]
37
+ @db.sqls.length.should == 0
38
+ end
39
+
40
+ specify "should handle plain symbols without a database query" do
41
+ @ds.select(:x).columns.should == [:x]
42
+ @db.sqls.length.should == 0
43
+ end
44
+
45
+ specify "should handle qualified symbols without a database query" do
46
+ @ds.select(:t__x).columns.should == [:x]
47
+ @db.sqls.length.should == 0
48
+ end
49
+
50
+ specify "should handle aliased symbols without a database query" do
51
+ @ds.select(:x___a).columns.should == [:a]
52
+ @db.sqls.length.should == 0
53
+ end
54
+
55
+ specify "should handle qualified and aliased symbols without a database query" do
56
+ @ds.select(:t__x___a).columns.should == [:a]
57
+ @db.sqls.length.should == 0
58
+ end
59
+
60
+ specify "should handle SQL::Identifiers " do
61
+ @ds.select(:x.identifier).columns.should == [:x]
62
+ @db.sqls.length.should == 0
63
+ end
64
+
65
+ specify "should handle SQL::QualifiedIdentifiers" do
66
+ @ds.select(:x.qualify(:t)).columns.should == [:x]
67
+ @ds.select(:x.identifier.qualify(:t)).columns.should == [:x]
68
+ @db.sqls.length.should == 0
69
+ end
70
+
71
+ specify "should handle SQL::AliasedExpressions" do
72
+ @ds.select(:x.as(:a)).columns.should == [:a]
73
+ @ds.select(:x.as(:a.identifier)).columns.should == [:a]
74
+ @db.sqls.length.should == 0
75
+ end
76
+
77
+ specify "should issue a database query if the wildcard is selected" do
78
+ @ds.columns
79
+ @db.sqls.length.should == 1
80
+ end
81
+
82
+ specify "should issue a database query if an unsupported type is used" do
83
+ @ds.select(1).columns
84
+ @db.sqls.length.should == 1
85
+ end
86
+
87
+ specify "should not have column introspection on by default" do
88
+ @db[:a].select(:x).columns
89
+ @db.sqls.length.should == 1
90
+ end
91
+ end
@@ -0,0 +1,64 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::DefaultsSetter" do
4
+ before do
5
+ @db = db = Sequel::Database.new
6
+ @c = c = Class.new(Sequel::Model(db[:foo]))
7
+ @c.instance_variable_set(:@db_schema, {:a=>{}})
8
+ @c.plugin :defaults_setter
9
+ @c.columns :a
10
+ @pr = proc{|x| db.meta_def(:schema){|*| [[:a, {:ruby_default => x}]]}; c.dataset = c.dataset; c}
11
+ end
12
+
13
+ it "should set default value upon initialization" do
14
+ @pr.call(2).new.values.should == {:a=>2}
15
+ end
16
+
17
+ it "should not mark the column as modified" do
18
+ @pr.call(2).new.changed_columns.should == []
19
+ end
20
+
21
+ it "should not set a default of nil" do
22
+ @pr.call(nil).new.values.should == {}
23
+ end
24
+
25
+ it "should not override a given value" do
26
+ @pr.call(2)
27
+ @c.new('a'=>3).values.should == {:a=>3}
28
+ @c.new('a'=>nil).values.should == {:a=>nil}
29
+ end
30
+
31
+ it "should work correctly when subclassing" do
32
+ Class.new(@pr.call(2)).new.values.should == {:a=>2}
33
+ end
34
+
35
+ it "should contain the default values in default_values" do
36
+ @pr.call(2).default_values.should == {:a=>2}
37
+ @pr.call(nil).default_values.should == {}
38
+ end
39
+
40
+ it "should allow modifications of default values" do
41
+ @pr.call(2)
42
+ @c.default_values[:a] = 3
43
+ @c.new.values.should == {:a => 3}
44
+ end
45
+
46
+ it "should allow proc default values" do
47
+ @pr.call(2)
48
+ @c.default_values[:a] = proc{3}
49
+ @c.new.values.should == {:a => 3}
50
+ end
51
+
52
+ it "should have procs that set default values set them to nil" do
53
+ @pr.call(2)
54
+ @c.default_values[:a] = proc{nil}
55
+ @c.new.values.should == {:a => nil}
56
+ end
57
+
58
+ it "should work correctly on a model without a dataset" do
59
+ @pr.call(2)
60
+ c = Class.new(Sequel::Model(@db[:bar]))
61
+ c.plugin :defaults_setter
62
+ c.default_values.should == {:a=>2}
63
+ end
64
+ end
@@ -13,6 +13,11 @@ describe "Sequel::Plugins::IdentityMap" do
13
13
  execute(sql)
14
14
  yield h
15
15
  end
16
+ def self.waw_identity_map(&block) # with and without
17
+ with_identity_map(&block)
18
+ db.reset
19
+ yield
20
+ end
16
21
  end
17
22
  class ::IdentityMapAlbum < ::IdentityMapModel
18
23
  columns :artist_id
@@ -216,4 +221,161 @@ describe "Sequel::Plugins::IdentityMap" do
216
221
  MODEL_DB.sqls.length.should == 2
217
222
  end
218
223
  end
224
+
225
+ it "should not override custom :eager_loaders for many_to_many associations" do
226
+ @c1.columns :id
227
+ @c2.columns :id
228
+ c = @c2
229
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa, :eager_loader=>(proc do |eo|
230
+ eo[:rows].each{|object| object.associations[:artists] = [c.load(:id=>object.id)]}
231
+ end)
232
+ ds = @c1.dataset
233
+ def ds.fetch_rows(sql)
234
+ execute(sql)
235
+ yield({:id=>1})
236
+ yield({:id=>2})
237
+ yield({:id=>3})
238
+ end
239
+
240
+ @c.waw_identity_map do
241
+ MODEL_DB.sqls.length.should == 0
242
+ a = @c1.eager(:artists).all
243
+ MODEL_DB.sqls.length.should == 1
244
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
245
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1)], [@c2.load(:id=>2)], [@c2.load(:id=>3)]]
246
+ MODEL_DB.sqls.length.should == 1
247
+ end
248
+ end
249
+
250
+ it "should work correctly when eagerly loading many_to_many associations" do
251
+ @c1.columns :id
252
+ @c2.columns :id
253
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa
254
+ ds = @c1.dataset
255
+ def ds.fetch_rows(sql)
256
+ execute(sql)
257
+ yield({:id=>1})
258
+ yield({:id=>2})
259
+ yield({:id=>3})
260
+ end
261
+ ds = @c2.dataset
262
+ def ds.fetch_rows(sql)
263
+ execute(sql)
264
+ yield({:id=>1, :x_foreign_key_x=>1})
265
+ yield({:id=>1, :x_foreign_key_x=>2})
266
+ yield({:id=>2, :x_foreign_key_x=>1})
267
+ yield({:id=>2, :x_foreign_key_x=>2})
268
+ yield({:id=>3, :x_foreign_key_x=>1})
269
+ yield({:id=>3, :x_foreign_key_x=>1})
270
+ end
271
+
272
+ @c.waw_identity_map do
273
+ MODEL_DB.sqls.length.should == 0
274
+ a = @c1.eager(:artists).all
275
+ MODEL_DB.sqls.length.should == 2
276
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
277
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
278
+ MODEL_DB.sqls.length.should == 2
279
+ end
280
+ end
281
+
282
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
283
+ @c1.columns :id, :id2
284
+ @c2.columns :id
285
+ @c1.set_primary_key :id, :id2
286
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>:artist_id, :join_table=>:aa
287
+ ds = @c1.dataset
288
+ def ds.fetch_rows(sql)
289
+ execute(sql)
290
+ yield({:id=>1, :id2=>4})
291
+ yield({:id=>2, :id2=>5})
292
+ yield({:id=>3, :id2=>6})
293
+ end
294
+ ds = @c2.dataset
295
+ def ds.fetch_rows(sql)
296
+ execute(sql)
297
+ yield({:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
298
+ yield({:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5})
299
+ yield({:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
300
+ yield({:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5})
301
+ yield({:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
302
+ yield({:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
303
+ end
304
+
305
+ @c.waw_identity_map do
306
+ MODEL_DB.sqls.length.should == 0
307
+ a = @c1.eager(:artists).all
308
+ MODEL_DB.sqls.length.should == 2
309
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
310
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
311
+ MODEL_DB.sqls.length.should == 2
312
+ end
313
+ end
314
+
315
+ it "should work correctly when eagerly loading many_through_many associations" do
316
+ @c1.columns :id
317
+ @c2.columns :id
318
+ @c1.plugin :many_through_many
319
+ @c1.many_through_many :artists, [[:aa, :album_id, :artist_id]], :class=>@c2
320
+ ds = @c1.dataset
321
+ def ds.fetch_rows(sql)
322
+ execute(sql)
323
+ yield({:id=>1})
324
+ yield({:id=>2})
325
+ yield({:id=>3})
326
+ end
327
+ ds = @c2.dataset
328
+ def ds.fetch_rows(sql)
329
+ execute(sql)
330
+ yield({:id=>1, :x_foreign_key_x=>1})
331
+ yield({:id=>1, :x_foreign_key_x=>2})
332
+ yield({:id=>2, :x_foreign_key_x=>1})
333
+ yield({:id=>2, :x_foreign_key_x=>2})
334
+ yield({:id=>3, :x_foreign_key_x=>1})
335
+ yield({:id=>3, :x_foreign_key_x=>1})
336
+ end
337
+
338
+ @c.waw_identity_map do
339
+ MODEL_DB.sqls.length.should == 0
340
+ a = @c1.eager(:artists).all
341
+ MODEL_DB.sqls.length.should == 2
342
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
343
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
344
+ MODEL_DB.sqls.length.should == 2
345
+ end
346
+ end
347
+
348
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
349
+ @c1.columns :id, :id2
350
+ @c2.columns :id
351
+ @c1.set_primary_key :id, :id2
352
+ @c1.plugin :many_through_many
353
+ @c1.many_through_many :artists, [[:aa, [:album_id1, :album_id2], :artist_id]], :class=>@c2
354
+ ds = @c1.dataset
355
+ def ds.fetch_rows(sql)
356
+ execute(sql)
357
+ yield({:id=>1, :id2=>4})
358
+ yield({:id=>2, :id2=>5})
359
+ yield({:id=>3, :id2=>6})
360
+ end
361
+ ds = @c2.dataset
362
+ def ds.fetch_rows(sql)
363
+ execute(sql)
364
+ yield({:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
365
+ yield({:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5})
366
+ yield({:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
367
+ yield({:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5})
368
+ yield({:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
369
+ yield({:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4})
370
+ end
371
+
372
+ @c.waw_identity_map do
373
+ MODEL_DB.sqls.length.should == 0
374
+ a = @c1.eager(:artists).all
375
+ MODEL_DB.sqls.length.should == 2
376
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
377
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
378
+ MODEL_DB.sqls.length.should == 2
379
+ end
380
+ end
219
381
  end