activerecord 5.2.0 → 5.2.1.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/lib/active_record/associations.rb +9 -9
  4. data/lib/active_record/associations/alias_tracker.rb +1 -1
  5. data/lib/active_record/associations/association.rb +17 -10
  6. data/lib/active_record/associations/belongs_to_association.rb +14 -5
  7. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -1
  8. data/lib/active_record/associations/builder/belongs_to.rb +11 -2
  9. data/lib/active_record/associations/collection_association.rb +10 -7
  10. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  11. data/lib/active_record/associations/has_one_association.rb +8 -0
  12. data/lib/active_record/associations/has_one_through_association.rb +5 -1
  13. data/lib/active_record/associations/join_dependency.rb +39 -64
  14. data/lib/active_record/associations/join_dependency/join_association.rb +12 -18
  15. data/lib/active_record/associations/join_dependency/join_part.rb +7 -0
  16. data/lib/active_record/associations/singular_association.rb +4 -10
  17. data/lib/active_record/associations/through_association.rb +1 -1
  18. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  19. data/lib/active_record/attribute_methods/read.rb +1 -1
  20. data/lib/active_record/autosave_association.rb +8 -3
  21. data/lib/active_record/callbacks.rb +4 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -4
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -12
  25. data/lib/active_record/connection_adapters/abstract/transaction.rb +23 -14
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +0 -11
  27. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -0
  28. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +4 -0
  30. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -0
  31. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +2 -2
  32. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -0
  33. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -1
  34. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +0 -3
  35. data/lib/active_record/counter_cache.rb +17 -13
  36. data/lib/active_record/gem_version.rb +2 -2
  37. data/lib/active_record/log_subscriber.rb +1 -1
  38. data/lib/active_record/persistence.rb +1 -1
  39. data/lib/active_record/query_cache.rb +4 -11
  40. data/lib/active_record/relation.rb +13 -13
  41. data/lib/active_record/relation/finder_methods.rb +2 -4
  42. data/lib/active_record/relation/merger.rb +2 -6
  43. data/lib/active_record/relation/predicate_builder.rb +6 -5
  44. data/lib/active_record/relation/query_methods.rb +16 -13
  45. data/lib/active_record/timestamp.rb +8 -1
  46. data/lib/active_record/transactions.rb +23 -20
  47. data/lib/active_record/type/serialized.rb +4 -0
  48. metadata +11 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a81ce578a98474e193314f6d806ee2f910d9d651fab445a14595154c41f6401
4
- data.tar.gz: 1a45823e0f6e0af4759af73c37e3a1b76bb9584e168b553bcfbca3208d2211e2
3
+ metadata.gz: f2af2945c4d69dad756447e4cc1d5b02414af7ab6c91ecff916a6a6278c5eee6
4
+ data.tar.gz: 708adbe157e758e9984681974e7114a8607d22fd47aef0e05fd2ad0f703f9ae8
5
5
  SHA512:
6
- metadata.gz: f6390719d7140cda11b175cfb8c4ad62e502204a202a70d6bf532131f323b49f120c30ca8e28d9e35335bb03d52a596ae7dcdfb3b05e762ae78150c70625adae
7
- data.tar.gz: cc85711ffcbec0a1b04b4f9940c0752687f9b98e7b65950cf46b28df14c13c9399d15699134bb3c5835bd76fdc8b944b0a661483c685075605b270f3bbe774d0
6
+ metadata.gz: bb2e18a8b6f04f0886ee175ff2666fa1f1d0fcad188962e433884c9f8f68b0e1a286dca098fbda9cb0122041efea542479aba0207e1af87c59a03f3bc850a5e0
7
+ data.tar.gz: c85d4ba7868c98156c16c85f13de074174d661866b8cf76e35f181ef62ec5f071627fcd52d368c581a72a791576bbb3753a3a73624f4e7221e61eae01cc041b7
@@ -1,3 +1,68 @@
1
+ ## Rails 5.2.1.rc1 (July 30, 2018) ##
2
+
3
+ * PostgreSQL: Support new relkind for partitioned tables.
4
+
5
+ Fixes #33008.
6
+
7
+ *Yannick Schutz*
8
+
9
+ * Rollback parent transaction when children fails to update.
10
+
11
+ *Guillaume Malette*
12
+
13
+ * Fix default value for MySQL time types with specified precision.
14
+
15
+ *Nikolay Kondratyev*
16
+
17
+ * Fix `touch` option to behave consistently with `Persistence#touch` method.
18
+
19
+ *Ryuta Kamizono*
20
+
21
+ * Fix `save` in `after_create_commit` won't invoke extra `after_create_commit`.
22
+
23
+ Fixes #32831.
24
+
25
+ *Ryuta Kamizono*
26
+
27
+ * Fix logic on disabling commit callbacks so they are not called unexpectedly when errors occur.
28
+
29
+ *Brian Durand*
30
+
31
+ * Fix parent record should not get saved with duplicate children records.
32
+
33
+ Fixes #32940.
34
+
35
+ *Santosh Wadghule*
36
+
37
+ * Fix that association's after_touch is not called with counter cache.
38
+
39
+ Fixes #31559.
40
+
41
+ *Ryuta Kamizono*
42
+
43
+ * `becomes` should clear the mutation tracker which is created in `after_initialize`.
44
+
45
+ Fixes #32867.
46
+
47
+ *Ryuta Kamizono*
48
+
49
+ * Allow a belonging to parent object to be created from a new record.
50
+
51
+ *Jolyon Pawlyn*
52
+
53
+ * Fix that building record with assigning multiple has_one associations
54
+ wrongly persists through record.
55
+
56
+ Fixes #32511.
57
+
58
+ *Sam DeCesare*
59
+
60
+ * Fix relation merging when one of the relations is going to skip the
61
+ query cache.
62
+
63
+ *James Williams*
64
+
65
+
1
66
  ## Rails 5.2.0 (April 09, 2018) ##
2
67
 
3
68
  * MySQL: Support mysql2 0.5.x.
@@ -241,7 +241,7 @@ module ActiveRecord
241
241
  association
242
242
  end
243
243
 
244
- def association_cached?(name) # :nodoc
244
+ def association_cached?(name) # :nodoc:
245
245
  @association_cache.key?(name)
246
246
  end
247
247
 
@@ -1232,9 +1232,9 @@ module ActiveRecord
1232
1232
  # * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
1233
1233
  # * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
1234
1234
  # * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
1235
- # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
1236
- # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
1237
- # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
1235
+ # * <tt>Firm#clients.build</tt> (similar to <tt>Client.new(firm_id: id)</tt>)
1236
+ # * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new(firm_id: id); c.save; c</tt>)
1237
+ # * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new(firm_id: id); c.save!</tt>)
1238
1238
  # * <tt>Firm#clients.reload</tt>
1239
1239
  # The declaration can also include an +options+ hash to specialize the behavior of the association.
1240
1240
  #
@@ -1405,9 +1405,9 @@ module ActiveRecord
1405
1405
  # An Account class declares <tt>has_one :beneficiary</tt>, which will add:
1406
1406
  # * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
1407
1407
  # * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
1408
- # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
1409
- # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
1410
- # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
1408
+ # * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new(account_id: id)</tt>)
1409
+ # * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new(account_id: id); b.save; b</tt>)
1410
+ # * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new(account_id: id); b.save!; b</tt>)
1411
1411
  # * <tt>Account#reload_beneficiary</tt>
1412
1412
  #
1413
1413
  # === Scopes
@@ -1746,8 +1746,8 @@ module ActiveRecord
1746
1746
  # * <tt>Developer#projects.size</tt>
1747
1747
  # * <tt>Developer#projects.find(id)</tt>
1748
1748
  # * <tt>Developer#projects.exists?(...)</tt>
1749
- # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
1750
- # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
1749
+ # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new(developer_id: id)</tt>)
1750
+ # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new(developer_id: id); c.save; c</tt>)
1751
1751
  # * <tt>Developer#projects.reload</tt>
1752
1752
  # The declaration may include an +options+ hash to specialize the behavior of the association.
1753
1753
  #
@@ -33,7 +33,7 @@ module ActiveRecord
33
33
  elsif join.is_a?(Arel::Nodes::Join)
34
34
  join.left.name == name ? 1 : 0
35
35
  elsif join.is_a?(Hash)
36
- join.fetch(name, 0)
36
+ join[name]
37
37
  else
38
38
  raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
39
39
  end
@@ -19,7 +19,6 @@ module ActiveRecord
19
19
  # HasManyThroughAssociation + ThroughAssociation
20
20
  class Association #:nodoc:
21
21
  attr_reader :owner, :target, :reflection
22
- attr_accessor :inversed
23
22
 
24
23
  delegate :options, to: :reflection
25
24
 
@@ -67,7 +66,7 @@ module ActiveRecord
67
66
  #
68
67
  # Note that if the target has not been loaded, it is not considered stale.
69
68
  def stale_target?
70
- !inversed && loaded? && @stale_state != stale_state
69
+ !@inversed && loaded? && @stale_state != stale_state
71
70
  end
72
71
 
73
72
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -98,23 +97,24 @@ module ActiveRecord
98
97
 
99
98
  # Set the inverse association, if possible
100
99
  def set_inverse_instance(record)
101
- if invertible_for?(record)
102
- inverse = record.association(inverse_reflection_for(record).name)
103
- inverse.target = owner
104
- inverse.inversed = true
100
+ if inverse = inverse_association_for(record)
101
+ inverse.inversed_from(owner)
105
102
  end
106
103
  record
107
104
  end
108
105
 
109
106
  # Remove the inverse association, if possible
110
107
  def remove_inverse_instance(record)
111
- if invertible_for?(record)
112
- inverse = record.association(inverse_reflection_for(record).name)
113
- inverse.target = nil
114
- inverse.inversed = false
108
+ if inverse = inverse_association_for(record)
109
+ inverse.inversed_from(nil)
115
110
  end
116
111
  end
117
112
 
113
+ def inversed_from(record)
114
+ self.target = record
115
+ @inversed = !!record
116
+ end
117
+
118
118
  # Returns the class of the target. belongs_to polymorphic overrides this to look at the
119
119
  # polymorphic_type field on the owner.
120
120
  def klass
@@ -240,6 +240,12 @@ module ActiveRecord
240
240
  end
241
241
  end
242
242
 
243
+ def inverse_association_for(record)
244
+ if invertible_for?(record)
245
+ record.association(inverse_reflection_for(record).name)
246
+ end
247
+ end
248
+
243
249
  # Can be redefined by subclasses, notably polymorphic belongs_to
244
250
  # The record parameter is necessary to support polymorphic inverses as we must check for
245
251
  # the association in the specific class of the record.
@@ -269,6 +275,7 @@ module ActiveRecord
269
275
  def build_record(attributes)
270
276
  reflection.build_association(attributes) do |record|
271
277
  initialize_attributes(record, attributes)
278
+ yield(record) if block_given?
272
279
  end
273
280
  end
274
281
 
@@ -26,10 +26,12 @@ module ActiveRecord
26
26
  decrement_counters
27
27
  end
28
28
 
29
+ replace_keys(record)
30
+
29
31
  self.target = record
30
32
  end
31
33
 
32
- def target=(record)
34
+ def inversed_from(record)
33
35
  replace_keys(record)
34
36
  super
35
37
  end
@@ -55,6 +57,10 @@ module ActiveRecord
55
57
  update_counters(1)
56
58
  end
57
59
 
60
+ def target_changed?
61
+ owner.saved_change_to_attribute?(reflection.foreign_key)
62
+ end
63
+
58
64
  private
59
65
 
60
66
  def update_counters(by)
@@ -78,19 +84,22 @@ module ActiveRecord
78
84
  def update_counters_on_replace(record)
79
85
  if require_counter_update? && different_target?(record)
80
86
  owner.instance_variable_set :@_after_replace_counter_called, true
81
- record.increment!(reflection.counter_cache_column)
87
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
82
88
  decrement_counters
83
89
  end
84
90
  end
85
91
 
86
92
  # Checks whether record is different to the current target, without loading it
87
93
  def different_target?(record)
88
- record.id != owner._read_attribute(reflection.foreign_key)
94
+ record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
89
95
  end
90
96
 
91
97
  def replace_keys(record)
92
- owner[reflection.foreign_key] = record ?
93
- record._read_attribute(reflection.association_primary_key(record.class)) : nil
98
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
99
+ end
100
+
101
+ def primary_key(record)
102
+ reflection.association_primary_key(record.class)
94
103
  end
95
104
 
96
105
  def foreign_key_present?
@@ -9,8 +9,11 @@ module ActiveRecord
9
9
  type.presence && type.constantize
10
10
  end
11
11
 
12
- private
12
+ def target_changed?
13
+ super || owner.saved_change_to_attribute?(reflection.foreign_type)
14
+ end
13
15
 
16
+ private
14
17
  def replace_keys(record)
15
18
  super
16
19
  owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
@@ -36,7 +36,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
36
36
 
37
37
  if (@_after_replace_counter_called ||= false)
38
38
  @_after_replace_counter_called = false
39
- elsif saved_change_to_attribute?(foreign_key) && !new_record?
39
+ elsif association(reflection.name).target_changed?
40
40
  if reflection.polymorphic?
41
41
  model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
42
  model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
@@ -49,14 +49,22 @@ module ActiveRecord::Associations::Builder # :nodoc:
49
49
  foreign_key = attribute_in_database foreign_key
50
50
 
51
51
  if foreign_key && model.respond_to?(:increment_counter)
52
+ foreign_key = counter_cache_target(reflection, model, foreign_key)
52
53
  model.increment_counter(cache_column, foreign_key)
53
54
  end
54
55
 
55
56
  if foreign_key_was && model_was.respond_to?(:decrement_counter)
57
+ foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
56
58
  model_was.decrement_counter(cache_column, foreign_key_was)
57
59
  end
58
60
  end
59
61
  end
62
+
63
+ private
64
+ def counter_cache_target(reflection, model, foreign_key)
65
+ primary_key = reflection.association_primary_key(model)
66
+ model.unscoped.where!(primary_key => foreign_key)
67
+ end
60
68
  end
61
69
  end
62
70
 
@@ -84,7 +92,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
84
92
  else
85
93
  klass = association.klass
86
94
  end
87
- old_record = klass.find_by(klass.primary_key => old_foreign_id)
95
+ primary_key = reflection.association_primary_key(klass)
96
+ old_record = klass.find_by(primary_key => old_foreign_id)
88
97
 
89
98
  if old_record
90
99
  if touch != true
@@ -103,9 +103,7 @@ module ActiveRecord
103
103
  if attributes.is_a?(Array)
104
104
  attributes.collect { |attr| build(attr, &block) }
105
105
  else
106
- add_to_target(build_record(attributes)) do |record|
107
- yield(record) if block_given?
108
- end
106
+ add_to_target(build_record(attributes, &block))
109
107
  end
110
108
  end
111
109
 
@@ -356,15 +354,18 @@ module ActiveRecord
356
354
  if attributes.is_a?(Array)
357
355
  attributes.collect { |attr| _create_record(attr, raise, &block) }
358
356
  else
357
+ record = build_record(attributes, &block)
359
358
  transaction do
360
- add_to_target(build_record(attributes)) do |record|
361
- yield(record) if block_given?
362
- insert_record(record, true, raise) {
359
+ result = nil
360
+ add_to_target(record) do
361
+ result = insert_record(record, true, raise) {
363
362
  @_was_loaded = loaded?
364
363
  @association_ids = nil
365
364
  }
366
365
  end
366
+ raise ActiveRecord::Rollback unless result
367
367
  end
368
+ record
368
369
  end
369
370
  end
370
371
 
@@ -442,7 +443,9 @@ module ActiveRecord
442
443
  end
443
444
  end
444
445
 
445
- result && records
446
+ raise ActiveRecord::Rollback unless result
447
+
448
+ records
446
449
  end
447
450
 
448
451
  def replace_on_target(record, index, skip_callbacks)
@@ -90,7 +90,7 @@ module ActiveRecord
90
90
  def build_record(attributes)
91
91
  ensure_not_nested
92
92
 
93
- record = super(attributes)
93
+ record = super
94
94
 
95
95
  inverse = source_reflection.inverse_of
96
96
  if inverse
@@ -107,6 +107,14 @@ module ActiveRecord
107
107
  yield
108
108
  end
109
109
  end
110
+
111
+ def _create_record(attributes, raise_error = false, &block)
112
+ unless owner.persisted?
113
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
114
+ end
115
+
116
+ super
117
+ end
110
118
  end
111
119
  end
112
120
  end
@@ -28,7 +28,11 @@ module ActiveRecord
28
28
  end
29
29
 
30
30
  if through_record
31
- through_record.update(attributes)
31
+ if through_record.new_record?
32
+ through_record.assign_attributes(attributes)
33
+ else
34
+ through_record.update(attributes)
35
+ end
32
36
  elsif owner.new_record? || !save
33
37
  through_proxy.build(attributes)
34
38
  else
@@ -67,63 +67,31 @@ module ActiveRecord
67
67
  end
68
68
  end
69
69
 
70
- # base is the base class on which operation is taking place.
71
- # associations is the list of associations which are joined using hash, symbol or array.
72
- # joins is the list of all string join commands and arel nodes.
73
- #
74
- # Example :
75
- #
76
- # class Physician < ActiveRecord::Base
77
- # has_many :appointments
78
- # has_many :patients, through: :appointments
79
- # end
80
- #
81
- # If I execute `@physician.patients.to_a` then
82
- # base # => Physician
83
- # associations # => []
84
- # joins # => [#<Arel::Nodes::InnerJoin: ...]
85
- #
86
- # However if I execute `Physician.joins(:appointments).to_a` then
87
- # base # => Physician
88
- # associations # => [:appointments]
89
- # joins # => []
90
- #
91
- def initialize(base, table, associations, alias_tracker)
92
- @alias_tracker = alias_tracker
70
+ def initialize(base, table, associations)
93
71
  tree = self.class.make_tree associations
94
72
  @join_root = JoinBase.new(base, table, build(tree, base))
95
- @join_root.children.each { |child| construct_tables! @join_root, child }
96
73
  end
97
74
 
98
75
  def reflections
99
76
  join_root.drop(1).map!(&:reflection)
100
77
  end
101
78
 
102
- def join_constraints(joins_to_add, join_type)
103
- joins = join_root.children.flat_map { |child|
104
- make_join_constraints(join_root, child, join_type)
105
- }
79
+ def join_constraints(joins_to_add, join_type, alias_tracker)
80
+ @alias_tracker = alias_tracker
81
+
82
+ construct_tables!(join_root)
83
+ joins = make_join_constraints(join_root, join_type)
106
84
 
107
85
  joins.concat joins_to_add.flat_map { |oj|
86
+ construct_tables!(oj.join_root)
108
87
  if join_root.match? oj.join_root
109
88
  walk join_root, oj.join_root
110
89
  else
111
- oj.join_root.children.flat_map { |child|
112
- make_join_constraints(oj.join_root, child, join_type)
113
- }
90
+ make_join_constraints(oj.join_root, join_type)
114
91
  end
115
92
  }
116
93
  end
117
94
 
118
- def aliases
119
- @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
120
- columns = join_part.column_names.each_with_index.map { |column_name, j|
121
- Aliases::Column.new column_name, "t#{i}_r#{j}"
122
- }
123
- Aliases::Table.new(join_part, columns)
124
- }
125
- end
126
-
127
95
  def instantiate(result_set, &block)
128
96
  primary_key = aliases.column_alias(join_root, join_root.primary_key)
129
97
 
@@ -155,28 +123,40 @@ module ActiveRecord
155
123
  parents.values
156
124
  end
157
125
 
126
+ def apply_column_aliases(relation)
127
+ relation._select!(-> { aliases.columns })
128
+ end
129
+
158
130
  protected
159
- attr_reader :alias_tracker, :base_klass, :join_root
131
+ attr_reader :alias_tracker, :join_root
160
132
 
161
133
  private
162
-
163
- def make_constraints(parent, child, tables, join_type)
164
- chain = child.reflection.chain
165
- foreign_table = parent.table
166
- foreign_klass = parent.base_klass
167
- child.join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
134
+ def aliases
135
+ @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
136
+ columns = join_part.column_names.each_with_index.map { |column_name, j|
137
+ Aliases::Column.new column_name, "t#{i}_r#{j}"
138
+ }
139
+ Aliases::Table.new(join_part, columns)
140
+ }
168
141
  end
169
142
 
170
- def make_outer_joins(parent, child)
171
- join_type = Arel::Nodes::OuterJoin
172
- make_join_constraints(parent, child, join_type, true)
143
+ def construct_tables!(join_root)
144
+ join_root.each_children do |parent, child|
145
+ child.tables = table_aliases_for(parent, child)
146
+ end
173
147
  end
174
148
 
175
- def make_join_constraints(parent, child, join_type, aliasing = false)
176
- tables = aliasing ? table_aliases_for(parent, child) : child.tables
177
- joins = make_constraints(parent, child, tables, join_type)
149
+ def make_join_constraints(join_root, join_type)
150
+ join_root.children.flat_map do |child|
151
+ make_constraints(join_root, child, join_type)
152
+ end
153
+ end
178
154
 
179
- joins.concat child.children.flat_map { |c| make_join_constraints(child, c, join_type, aliasing) }
155
+ def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
156
+ foreign_table = parent.table
157
+ foreign_klass = parent.base_klass
158
+ joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
159
+ joins.concat child.children.flat_map { |c| make_constraints(child, c, join_type) }
180
160
  end
181
161
 
182
162
  def table_aliases_for(parent, node)
@@ -189,11 +169,6 @@ module ActiveRecord
189
169
  }
190
170
  end
191
171
 
192
- def construct_tables!(parent, node)
193
- node.tables = table_aliases_for(parent, node)
194
- node.children.each { |child| construct_tables! node, child }
195
- end
196
-
197
172
  def table_alias_for(reflection, parent, join)
198
173
  name = "#{reflection.plural_name}_#{parent.table_name}"
199
174
  join ? "#{name}_join" : name
@@ -204,8 +179,8 @@ module ActiveRecord
204
179
  [left.children.find { |node2| node1.match? node2 }, node1]
205
180
  }.partition(&:first)
206
181
 
207
- ojs = missing.flat_map { |_, n| make_outer_joins left, n }
208
- intersection.flat_map { |l, r| walk l, r }.concat ojs
182
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
183
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
209
184
  end
210
185
 
211
186
  def find_reflection(klass, name)
@@ -223,7 +198,7 @@ module ActiveRecord
223
198
  raise EagerLoadPolymorphicError.new(reflection)
224
199
  end
225
200
 
226
- JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker)
201
+ JoinAssociation.new(reflection, build(right, reflection.klass))
227
202
  end
228
203
  end
229
204
 
@@ -248,7 +223,7 @@ module ActiveRecord
248
223
  next
249
224
  end
250
225
 
251
- model = seen[ar_parent.object_id][node.base_klass][id]
226
+ model = seen[ar_parent.object_id][node][id]
252
227
 
253
228
  if model
254
229
  construct(model, node, row, rs, seen, model_cache, aliases)
@@ -260,7 +235,7 @@ module ActiveRecord
260
235
  model.readonly!
261
236
  end
262
237
 
263
- seen[ar_parent.object_id][node.base_klass][id] = model
238
+ seen[ar_parent.object_id][node][id] = model
264
239
  construct(model, node, row, rs, seen, model_cache, aliases)
265
240
  end
266
241
  end