activerecord 6.0.3.7 → 6.0.4

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -0
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/association.rb +2 -3
  5. data/lib/active_record/associations/association_scope.rb +7 -1
  6. data/lib/active_record/associations/collection_association.rb +7 -0
  7. data/lib/active_record/associations/collection_proxy.rb +1 -0
  8. data/lib/active_record/associations/join_dependency.rb +14 -6
  9. data/lib/active_record/associations/join_dependency/join_association.rb +7 -0
  10. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  11. data/lib/active_record/associations/preloader.rb +6 -2
  12. data/lib/active_record/associations/preloader/association.rb +41 -23
  13. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  14. data/lib/active_record/associations/through_association.rb +1 -1
  15. data/lib/active_record/autosave_association.rb +10 -10
  16. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -0
  17. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
  18. data/lib/active_record/connection_adapters/abstract_adapter.rb +6 -0
  19. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +9 -4
  20. data/lib/active_record/connection_adapters/mysql/database_statements.rb +1 -1
  21. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -4
  22. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
  23. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +8 -0
  24. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  25. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
  26. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +1 -1
  27. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -1
  28. data/lib/active_record/core.rb +5 -6
  29. data/lib/active_record/enum.rb +13 -6
  30. data/lib/active_record/fixture_set/render_context.rb +1 -1
  31. data/lib/active_record/gem_version.rb +2 -2
  32. data/lib/active_record/insert_all.rb +1 -1
  33. data/lib/active_record/locking/optimistic.rb +9 -0
  34. data/lib/active_record/model_schema.rb +29 -0
  35. data/lib/active_record/reflection.rb +11 -13
  36. data/lib/active_record/relation.rb +6 -3
  37. data/lib/active_record/relation/calculations.rb +1 -1
  38. data/lib/active_record/relation/delegation.rb +2 -1
  39. data/lib/active_record/relation/finder_methods.rb +1 -1
  40. data/lib/active_record/relation/merger.rb +7 -2
  41. data/lib/active_record/relation/query_methods.rb +23 -11
  42. data/lib/active_record/scoping/named.rb +5 -0
  43. data/lib/active_record/test_fixtures.rb +19 -1
  44. data/lib/active_record/type/time.rb +10 -0
  45. data/lib/active_record/validations/associated.rb +1 -1
  46. metadata +12 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcd8faaaae67978bbcad0411b20e03f1ea5c805fd21333b9d6e0567a5474012e
4
- data.tar.gz: 6badedd47d9d3fc5c9e0540a061083f8c5a120915de769a845d64582ba360820
3
+ metadata.gz: 1fc6a79e2176d3edc373f3f920c287f8f1bd35123e3af17f52034282f4f15735
4
+ data.tar.gz: 91b8b7c4107316e3b09b3176acf6b7fea744976df609f292b192033923962a4c
5
5
  SHA512:
6
- metadata.gz: 203838df8b2326e8f95ce6c37b444d5af091e76a05c81065d466b599b806a38d706ffc47f04614037795810cdc66869bf2e04782f2f115694ed2f2cc2bbc4cd8
7
- data.tar.gz: 1e413a33bf013b594a877c785ac79338b4060f4c18a4d2d361e37a2d5b138d1c77ca2c5c98d9495676c8e561529b8d56ce620eec99d7c4c88924a1eb49388318
6
+ metadata.gz: ad92ac3aaab6f497944161e777415d90cb0c1e61d8d297d571d18cb017d430a8bbf0dc97289cec4f19620e0421641c42917c4bbeaba9fe4638c0601d1c0c50ed
7
+ data.tar.gz: b8094270448f085b549609e682be83489ab08a0d56f6b7946d6f525b4c20f32bf8847dc89a0696ae7e63231213cddbac16a78cc17a43f1c2591063f34a07644d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,111 @@
1
+ ## Rails 6.0.4 (June 15, 2021) ##
2
+
3
+ * Only warn about negative enums if a positive form that would cause conflicts exists.
4
+
5
+ Fixes #39065.
6
+
7
+ *Alex Ghiculescu*
8
+
9
+ * Allow the inverse of a `has_one` association that was previously autosaved to be loaded.
10
+
11
+ Fixes #34255.
12
+
13
+ *Steven Weber*
14
+
15
+ * Reset statement cache for association if `table_name` is changed.
16
+
17
+ Fixes #36453.
18
+
19
+ *Ryuta Kamizono*
20
+
21
+ * Type cast extra select for eager loading.
22
+
23
+ *Ryuta Kamizono*
24
+
25
+ * Prevent collection associations from being autosaved multiple times.
26
+
27
+ Fixes #39173.
28
+
29
+ *Eugene Kenny*
30
+
31
+ * Resolve issue with insert_all unique_by option when used with expression index.
32
+
33
+ When the `:unique_by` option of `ActiveRecord::Persistence.insert_all` and
34
+ `ActiveRecord::Persistence.upsert_all` was used with the name of an expression index, an error
35
+ was raised. Adding a guard around the formatting behavior for the `:unique_by` corrects this.
36
+
37
+ Usage:
38
+
39
+ ```ruby
40
+ create_table :books, id: :integer, force: true do |t|
41
+ t.column :name, :string
42
+ t.index "lower(name)", unique: true
43
+ end
44
+
45
+ Book.insert_all [{ name: "MyTest" }], unique_by: :index_books_on_lower_name
46
+ ```
47
+
48
+ Fixes #39516.
49
+
50
+ *Austen Madden*
51
+
52
+ * Fix preloading for polymorphic association with custom scope.
53
+
54
+ *Ryuta Kamizono*
55
+
56
+ * Allow relations with different SQL comments in the `or` method.
57
+
58
+ *Takumi Shotoku*
59
+
60
+ * Resolve conflict between counter cache and optimistic locking.
61
+
62
+ Bump an Active Record instance's lock version after updating its counter
63
+ cache. This avoids raising an unnecessary `ActiveRecord::StaleObjectError`
64
+ upon subsequent transactions by maintaining parity with the corresponding
65
+ database record's `lock_version` column.
66
+
67
+ Fixes #16449.
68
+
69
+ *Aaron Lipman*
70
+
71
+ * Fix through association with source/through scope which has joins.
72
+
73
+ *Ryuta Kamizono*
74
+
75
+ * Fix through association to respect source scope for includes/preload.
76
+
77
+ *Ryuta Kamizono*
78
+
79
+ * Fix eager load with Arel joins to maintain the original joins order.
80
+
81
+ *Ryuta Kamizono*
82
+
83
+ * Fix group by count with eager loading + order + limit/offset.
84
+
85
+ *Ryuta Kamizono*
86
+
87
+ * Fix left joins order when merging multiple left joins from different associations.
88
+
89
+ *Ryuta Kamizono*
90
+
91
+ * Fix index creation to preserve index comment in bulk change table on MySQL.
92
+
93
+ *Ryuta Kamizono*
94
+
95
+ * Change `remove_foreign_key` to not check `:validate` option if database
96
+ doesn't support the feature.
97
+
98
+ *Ryuta Kamizono*
99
+
100
+ * Fix the result of aggregations to maintain duplicated "group by" fields.
101
+
102
+ *Ryuta Kamizono*
103
+
104
+ * Do not return duplicated records when using preload.
105
+
106
+ *Bogdan Gusiev*
107
+
108
+
1
109
  ## Rails 6.0.3.7 (May 05, 2021) ##
2
110
 
3
111
  * No changes.
data/README.rdoc CHANGED
@@ -194,7 +194,7 @@ The latest version of Active Record can be installed with RubyGems:
194
194
 
195
195
  Source code can be downloaded as part of the Rails project on GitHub:
196
196
 
197
- * https://github.com/rails/rails/tree/master/activerecord
197
+ * https://github.com/rails/rails/tree/main/activerecord
198
198
 
199
199
 
200
200
  == License
@@ -204,14 +204,13 @@ module ActiveRecord
204
204
  scope = self.scope
205
205
  return scope.to_a if skip_statement_cache?(scope)
206
206
 
207
- conn = klass.connection
208
- sc = reflection.association_scope_cache(conn, owner) do |params|
207
+ sc = reflection.association_scope_cache(klass, owner) do |params|
209
208
  as = AssociationScope.create { params.bind }
210
209
  target_scope.merge!(as.scope(self))
211
210
  end
212
211
 
213
212
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
214
- sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
213
+ sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) } || []
215
214
  end
216
215
 
217
216
  # The scope for this association.
@@ -52,7 +52,7 @@ module ActiveRecord
52
52
  attr_reader :value_transformation
53
53
 
54
54
  def join(table, constraint)
55
- table.create_join(table, table.create_on(constraint))
55
+ table.create_join(table, table.create_on(constraint), Arel::Nodes::LeadingJoin)
56
56
  end
57
57
 
58
58
  def last_chain_scope(scope, reflection, owner)
@@ -134,6 +134,12 @@ module ActiveRecord
134
134
 
135
135
  if scope_chain_item == chain_head.scope
136
136
  scope.merge! item.except(:where, :includes, :unscope, :order)
137
+ elsif !item.references_values.empty?
138
+ join_dependency = item.construct_join_dependency(
139
+ item.eager_load_values | item.includes_values, Arel::Nodes::OuterJoin
140
+ )
141
+ scope.joins!(*item.joins_values, join_dependency)
142
+ scope.left_outer_joins!(*item.left_outer_joins_values)
137
143
  end
138
144
 
139
145
  reflection.all_includes do
@@ -332,6 +332,13 @@ module ActiveRecord
332
332
  persisted + memory
333
333
  end
334
334
 
335
+ def build_record(attributes)
336
+ previous = klass.current_scope(true) if block_given?
337
+ super
338
+ ensure
339
+ klass.current_scope = previous if previous
340
+ end
341
+
335
342
  def _create_record(attributes, raise = false, &block)
336
343
  unless owner.persisted?
337
344
  raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
@@ -51,6 +51,7 @@ module ActiveRecord
51
51
  def loaded?
52
52
  @association.loaded?
53
53
  end
54
+ alias :loaded :loaded?
54
55
 
55
56
  ##
56
57
  # :method: select
@@ -106,8 +106,16 @@ module ActiveRecord
106
106
  model_cache = Hash.new { |h, klass| h[klass] = {} }
107
107
  parents = model_cache[join_root]
108
108
 
109
- column_aliases = aliases.column_aliases join_root
110
- column_aliases += explicit_selections(column_aliases, result_set)
109
+ column_aliases = aliases.column_aliases(join_root)
110
+ column_names = explicit_selections(column_aliases, result_set)
111
+
112
+ if column_names.empty?
113
+ column_types = {}
114
+ else
115
+ column_types = result_set.column_types
116
+ column_types = column_types.slice(*column_names) unless column_types.empty?
117
+ column_aliases += column_names.map! { |name| Aliases::Column.new(name, name) }
118
+ end
111
119
 
112
120
  message_bus = ActiveSupport::Notifications.instrumenter
113
121
 
@@ -119,7 +127,7 @@ module ActiveRecord
119
127
  message_bus.instrument("instantiation.active_record", payload) do
120
128
  result_set.each { |row_hash|
121
129
  parent_key = primary_key ? row_hash[primary_key] : row_hash
122
- parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
130
+ parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
123
131
  construct(parent, join_root, row_hash, seen, model_cache)
124
132
  }
125
133
  end
@@ -139,9 +147,9 @@ module ActiveRecord
139
147
 
140
148
  def explicit_selections(root_column_aliases, result_set)
141
149
  root_names = root_column_aliases.map(&:name).to_set
142
- result_set.columns
143
- .reject { |n| root_names.include?(n) || n =~ /\At\d+_r\d+\z/ }
144
- .map { |n| Aliases::Column.new(n, n) }
150
+ result_set.columns.each_with_object([]) do |name, result|
151
+ result << name unless /\At\d+_r\d+\z/.match?(name) || root_names.include?(name)
152
+ end
145
153
  end
146
154
 
147
155
  def aliases
@@ -33,6 +33,13 @@ module ActiveRecord
33
33
 
34
34
  join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
35
35
 
36
+ unless join_scope.references_values.empty?
37
+ join_dependency = join_scope.construct_join_dependency(
38
+ join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
39
+ )
40
+ join_scope.joins!(join_dependency)
41
+ end
42
+
36
43
  arel = join_scope.arel(alias_tracker.aliases)
37
44
  nodes = arel.constraints.first
38
45
 
@@ -62,8 +62,8 @@ module ActiveRecord
62
62
  hash
63
63
  end
64
64
 
65
- def instantiate(row, aliases, &block)
66
- base_klass.instantiate(extract_record(row, aliases), &block)
65
+ def instantiate(row, aliases, column_types = {}, &block)
66
+ base_klass.instantiate(extract_record(row, aliases), column_types, &block)
67
67
  end
68
68
  end
69
69
  end
@@ -94,6 +94,10 @@ module ActiveRecord
94
94
  end
95
95
  end
96
96
 
97
+ def initialize(associate_by_default: true)
98
+ @associate_by_default = associate_by_default
99
+ end
100
+
97
101
  private
98
102
  # Loads all the given data into +records+ for the +association+.
99
103
  def preloaders_on(association, records, scope, polymorphic_parent = false)
@@ -142,7 +146,7 @@ module ActiveRecord
142
146
 
143
147
  def preloaders_for_reflection(reflection, records, scope)
144
148
  records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
145
- preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
149
+ preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope, @associate_by_default).run
146
150
  end
147
151
  end
148
152
 
@@ -157,7 +161,7 @@ module ActiveRecord
157
161
  end
158
162
 
159
163
  class AlreadyLoaded # :nodoc:
160
- def initialize(klass, owners, reflection, preload_scope)
164
+ def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
161
165
  @owners = owners
162
166
  @reflection = reflection
163
167
  end
@@ -4,46 +4,62 @@ module ActiveRecord
4
4
  module Associations
5
5
  class Preloader
6
6
  class Association #:nodoc:
7
- def initialize(klass, owners, reflection, preload_scope)
7
+ def initialize(klass, owners, reflection, preload_scope, associate_by_default = true)
8
8
  @klass = klass
9
- @owners = owners
9
+ @owners = owners.uniq(&:__id__)
10
10
  @reflection = reflection
11
11
  @preload_scope = preload_scope
12
+ @associate = associate_by_default || !preload_scope || preload_scope.empty_scope?
12
13
  @model = owners.first && owners.first.class
13
14
  end
14
15
 
15
16
  def run
16
- if !preload_scope || preload_scope.empty_scope?
17
- owners.each do |owner|
18
- associate_records_to_owner(owner, records_by_owner[owner] || [])
19
- end
20
- else
21
- # Custom preload scope is used and
22
- # the association can not be marked as loaded
23
- # Loading into a Hash instead
24
- records_by_owner
25
- end
17
+ records = records_by_owner
18
+
19
+ owners.each do |owner|
20
+ associate_records_to_owner(owner, records[owner] || [])
21
+ end if @associate
22
+
26
23
  self
27
24
  end
28
25
 
29
26
  def records_by_owner
30
- # owners can be duplicated when a relation has a collection association join
31
- # #compare_by_identity makes such owners different hash keys
32
- @records_by_owner ||= preloaded_records.each_with_object({}.compare_by_identity) do |record, result|
33
- owners_by_key[convert_key(record[association_key_name])].each do |owner|
34
- (result[owner] ||= []) << record
35
- end
36
- end
27
+ load_records unless defined?(@records_by_owner)
28
+
29
+ @records_by_owner
37
30
  end
38
31
 
39
32
  def preloaded_records
40
- return @preloaded_records if defined?(@preloaded_records)
41
- @preloaded_records = owner_keys.empty? ? [] : records_for(owner_keys)
33
+ load_records unless defined?(@preloaded_records)
34
+
35
+ @preloaded_records
42
36
  end
43
37
 
44
38
  private
45
39
  attr_reader :owners, :reflection, :preload_scope, :model, :klass
46
40
 
41
+ def load_records
42
+ # owners can be duplicated when a relation has a collection association join
43
+ # #compare_by_identity makes such owners different hash keys
44
+ @records_by_owner = {}.compare_by_identity
45
+ raw_records = owner_keys.empty? ? [] : records_for(owner_keys)
46
+
47
+ @preloaded_records = raw_records.select do |record|
48
+ assignments = []
49
+
50
+ owners_by_key[convert_key(record[association_key_name])].each do |owner|
51
+ entries = (@records_by_owner[owner] ||= [])
52
+
53
+ if reflection.collection? || entries.empty?
54
+ entries << record
55
+ assignments << record
56
+ end
57
+ end
58
+
59
+ !assignments.empty?
60
+ end
61
+ end
62
+
47
63
  # The name of the key on the associated records
48
64
  def association_key_name
49
65
  reflection.join_primary_key(klass)
@@ -113,7 +129,9 @@ module ActiveRecord
113
129
  end
114
130
 
115
131
  def reflection_scope
116
- @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
132
+ @reflection_scope ||= begin
133
+ reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass).inject(&:merge!) || klass.unscoped
134
+ end
117
135
  end
118
136
 
119
137
  def build_scope
@@ -123,7 +141,7 @@ module ActiveRecord
123
141
  scope.where!(reflection.type => model.polymorphic_name)
124
142
  end
125
143
 
126
- scope.merge!(reflection_scope) if reflection.scope
144
+ scope.merge!(reflection_scope) unless reflection_scope.empty_scope?
127
145
  scope.merge!(preload_scope) if preload_scope
128
146
  scope
129
147
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module Associations
5
5
  class Preloader
6
6
  class ThroughAssociation < Association # :nodoc:
7
- PRELOADER = ActiveRecord::Associations::Preloader.new
7
+ PRELOADER = ActiveRecord::Associations::Preloader.new(associate_by_default: false)
8
8
 
9
9
  def initialize(*)
10
10
  super
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  reflection.chain.drop(1).each do |reflection|
33
33
  relation = reflection.klass.scope_for_association
34
34
  scope.merge!(
35
- relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
35
+ relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
36
36
  )
37
37
  end
38
38
  scope
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  # == Callbacks
30
30
  #
31
31
  # Association with autosave option defines several callbacks on your
32
- # model (before_save, after_create, after_update). Please note that
32
+ # model (around_save, before_save, after_create, after_update). Please note that
33
33
  # callbacks are executed in the order they were defined in
34
34
  # model. You should avoid modifying the association content, before
35
35
  # autosave callbacks are executed. Placing your callbacks after
@@ -180,8 +180,7 @@ module ActiveRecord
180
180
  save_method = :"autosave_associated_records_for_#{reflection.name}"
181
181
 
182
182
  if reflection.collection?
183
- before_save :before_save_collection_association
184
- after_save :after_save_collection_association
183
+ around_save :around_save_collection_association
185
184
 
186
185
  define_non_cyclic_method(save_method) { save_collection_association(reflection) }
187
186
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
@@ -358,14 +357,15 @@ module ActiveRecord
358
357
  end
359
358
  end
360
359
 
361
- # Is used as a before_save callback to check while saving a collection
360
+ # Is used as an around_save callback to check while saving a collection
362
361
  # association whether or not the parent was a new record before saving.
363
- def before_save_collection_association
364
- @new_record_before_save ||= new_record?
365
- end
362
+ def around_save_collection_association
363
+ previously_new_record_before_save = (@new_record_before_save ||= false)
364
+ @new_record_before_save = !previously_new_record_before_save && new_record?
366
365
 
367
- def after_save_collection_association
368
- @new_record_before_save = false
366
+ yield
367
+ ensure
368
+ @new_record_before_save = previously_new_record_before_save
369
369
  end
370
370
 
371
371
  # Saves any new associated records, or all loaded autosave associations if
@@ -444,7 +444,7 @@ module ActiveRecord
444
444
  unless reflection.through_reflection
445
445
  record[reflection.foreign_key] = key
446
446
  if inverse_reflection = reflection.inverse_of
447
- record.association(inverse_reflection.name).loaded!
447
+ record.association(inverse_reflection.name).inversed_from(self)
448
448
  end
449
449
  end
450
450