activerecord 7.1.1 → 7.1.5
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +496 -0
- data/README.rdoc +1 -0
- data/lib/active_record/associations/association.rb +2 -1
- data/lib/active_record/associations/belongs_to_association.rb +4 -4
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -12
- data/lib/active_record/associations/preloader/association.rb +4 -1
- data/lib/active_record/associations.rb +21 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +15 -11
- data/lib/active_record/attribute_methods/read.rb +3 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +47 -7
- data/lib/active_record/autosave_association.rb +5 -2
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +19 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -19
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -33
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +25 -21
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +49 -10
- data/lib/active_record/counter_cache.rb +7 -3
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/hash_config.rb +6 -2
- data/lib/active_record/delegated_type.rb +7 -7
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +6 -2
- data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +6 -9
- data/lib/active_record/errors.rb +5 -4
- data/lib/active_record/fixtures.rb +16 -0
- data/lib/active_record/future_result.rb +10 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/internal_metadata.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +14 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +9 -5
- data/lib/active_record/model_schema.rb +12 -7
- data/lib/active_record/nested_attributes.rb +16 -5
- data/lib/active_record/normalization.rb +8 -0
- data/lib/active_record/persistence.rb +6 -5
- data/lib/active_record/promise.rb +1 -1
- data/lib/active_record/query_cache.rb +1 -1
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/railtie.rb +14 -14
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +7 -7
- data/lib/active_record/reflection.rb +21 -3
- data/lib/active_record/relation/calculations.rb +28 -1
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/query_methods.rb +21 -7
- data/lib/active_record/relation.rb +30 -5
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +35 -13
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +3 -1
- data/lib/active_record.rb +2 -2
- data/lib/arel/nodes/homogeneous_in.rb +1 -1
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/to_sql.rb +2 -1
- metadata +14 -13
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
|
65
65
|
updates.merge!(touch_updates)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
|
|
68
|
+
unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
|
|
69
69
|
|
|
70
70
|
true
|
|
71
71
|
end
|
|
@@ -112,6 +112,7 @@ module ActiveRecord
|
|
|
112
112
|
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
|
113
113
|
# # WHERE id IN (10, 15)
|
|
114
114
|
def update_counters(id, counters)
|
|
115
|
+
id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
|
|
115
116
|
unscoped.where!(primary_key => id).update_counters(counters)
|
|
116
117
|
end
|
|
117
118
|
|
|
@@ -198,8 +199,7 @@ module ActiveRecord
|
|
|
198
199
|
|
|
199
200
|
if affected_rows > 0
|
|
200
201
|
each_counter_cached_associations do |association|
|
|
201
|
-
foreign_key
|
|
202
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
|
202
|
+
unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
|
|
203
203
|
association.decrement_counters
|
|
204
204
|
end
|
|
205
205
|
end
|
|
@@ -213,5 +213,9 @@ module ActiveRecord
|
|
|
213
213
|
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
|
214
214
|
end
|
|
215
215
|
end
|
|
216
|
+
|
|
217
|
+
def _foreign_keys_equal?(fkey1, fkey2)
|
|
218
|
+
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
|
219
|
+
end
|
|
216
220
|
end
|
|
217
221
|
end
|
|
@@ -114,8 +114,12 @@ module ActiveRecord
|
|
|
114
114
|
configuration_hash[:schema_cache_path]
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
def default_schema_cache_path
|
|
118
|
-
|
|
117
|
+
def default_schema_cache_path(db_dir = "db")
|
|
118
|
+
if primary?
|
|
119
|
+
File.join(db_dir, "schema_cache.yml")
|
|
120
|
+
else
|
|
121
|
+
File.join(db_dir, "#{name}_schema_cache.yml")
|
|
122
|
+
end
|
|
119
123
|
end
|
|
120
124
|
|
|
121
125
|
def lazy_schema_cache_path
|
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
|
36
36
|
#
|
|
37
37
|
# Let's look at that entry/message/comment example using delegated types:
|
|
38
38
|
#
|
|
39
|
-
# # Schema: entries[ id, account_id, creator_id,
|
|
39
|
+
# # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
|
|
40
40
|
# class Entry < ApplicationRecord
|
|
41
41
|
# belongs_to :account
|
|
42
42
|
# belongs_to :creator
|
|
@@ -51,12 +51,12 @@ module ActiveRecord
|
|
|
51
51
|
# end
|
|
52
52
|
# end
|
|
53
53
|
#
|
|
54
|
-
# # Schema: messages[ id, subject, body ]
|
|
54
|
+
# # Schema: messages[ id, subject, body, created_at, updated_at ]
|
|
55
55
|
# class Message < ApplicationRecord
|
|
56
56
|
# include Entryable
|
|
57
57
|
# end
|
|
58
58
|
#
|
|
59
|
-
# # Schema: comments[ id, content ]
|
|
59
|
+
# # Schema: comments[ id, content, created_at, updated_at ]
|
|
60
60
|
# class Comment < ApplicationRecord
|
|
61
61
|
# include Entryable
|
|
62
62
|
# end
|
|
@@ -102,14 +102,14 @@ module ActiveRecord
|
|
|
102
102
|
# You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
|
|
103
103
|
# like so:
|
|
104
104
|
#
|
|
105
|
-
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
|
|
105
|
+
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
|
|
106
106
|
#
|
|
107
107
|
# If you need more complicated composition, or you need to perform dependent validation, you should build a factory
|
|
108
108
|
# method or class to take care of the complicated needs. This could be as simple as:
|
|
109
109
|
#
|
|
110
110
|
# class Entry < ApplicationRecord
|
|
111
|
-
# def self.create_with_comment(content, creator: Current.user)
|
|
112
|
-
# create! entryable: Comment.new(content: content), creator: creator
|
|
111
|
+
# def self.create_with_comment(content, creator: Current.user, account: Current.account)
|
|
112
|
+
# create! entryable: Comment.new(content: content), creator: creator, account: account
|
|
113
113
|
# end
|
|
114
114
|
# end
|
|
115
115
|
#
|
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
|
138
138
|
#
|
|
139
139
|
# Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
|
|
140
140
|
#
|
|
141
|
-
# == Nested Attributes
|
|
141
|
+
# == Nested \Attributes
|
|
142
142
|
#
|
|
143
143
|
# Enabling nested attributes on a delegated_type association allows you to
|
|
144
144
|
# create the entry and message in one go:
|
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
|
19
19
|
)
|
|
20
20
|
association_model = association_class.constantize
|
|
21
21
|
owner_class = owner_model_name.constantize
|
|
22
|
-
owner = owner_class.find_by(owner_class.primary_key
|
|
22
|
+
owner = owner_class.find_by(owner_class.primary_key => [owner_id])
|
|
23
23
|
|
|
24
24
|
if !owner_destroyed?(owner, ensuring_owner_was_method)
|
|
25
25
|
raise DestroyAssociationAsyncError, "owner record not destroyed"
|
|
@@ -144,7 +144,13 @@ module ActiveRecord
|
|
|
144
144
|
|
|
145
145
|
# Returns whether a given attribute is encrypted or not.
|
|
146
146
|
def encrypted_attribute?(attribute_name)
|
|
147
|
-
|
|
147
|
+
name = attribute_name.to_s
|
|
148
|
+
name = self.class.attribute_aliases[name] || name
|
|
149
|
+
|
|
150
|
+
return false unless self.class.encrypted_attributes&.include? name.to_sym
|
|
151
|
+
|
|
152
|
+
type = type_for_attribute(name)
|
|
153
|
+
type.encrypted? read_attribute_before_type_cast(name)
|
|
148
154
|
end
|
|
149
155
|
|
|
150
156
|
# Returns the ciphertext for +attribute_name+.
|
|
@@ -44,6 +44,10 @@ module ActiveRecord
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def encrypted?(value)
|
|
48
|
+
with_context { encryptor.encrypted? value }
|
|
49
|
+
end
|
|
50
|
+
|
|
47
51
|
def changed_in_place?(raw_old_value, new_value)
|
|
48
52
|
old_value = raw_old_value.nil? ? nil : deserialize(raw_old_value)
|
|
49
53
|
old_value != new_value
|
|
@@ -136,11 +140,11 @@ module ActiveRecord
|
|
|
136
140
|
end
|
|
137
141
|
|
|
138
142
|
def encryption_options
|
|
139
|
-
|
|
143
|
+
{ key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
|
|
140
144
|
end
|
|
141
145
|
|
|
142
146
|
def decryption_options
|
|
143
|
-
|
|
147
|
+
{ key_provider: key_provider }.compact
|
|
144
148
|
end
|
|
145
149
|
|
|
146
150
|
def clean_text_scheme
|
|
@@ -28,7 +28,6 @@ module ActiveRecord
|
|
|
28
28
|
ActiveRecord::Relation.prepend(RelationQueries)
|
|
29
29
|
ActiveRecord::Base.include(CoreQueries)
|
|
30
30
|
ActiveRecord::Encryption::EncryptedAttributeType.prepend(ExtendedEncryptableType)
|
|
31
|
-
Arel::Nodes::HomogeneousIn.prepend(InWithAdditionalValues)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# When modifying this file run performance tests in
|
|
@@ -153,20 +152,6 @@ module ActiveRecord
|
|
|
153
152
|
end
|
|
154
153
|
end
|
|
155
154
|
end
|
|
156
|
-
|
|
157
|
-
module InWithAdditionalValues
|
|
158
|
-
def proc_for_binds
|
|
159
|
-
-> value { ActiveModel::Attribute.with_cast_value(attribute.name, value, encryption_aware_type_caster) }
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def encryption_aware_type_caster
|
|
163
|
-
if attribute.type_caster.is_a?(ActiveRecord::Encryption::EncryptedAttributeType)
|
|
164
|
-
attribute.type_caster.cast_type
|
|
165
|
-
else
|
|
166
|
-
attribute.type_caster
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
155
|
end
|
|
171
156
|
end
|
|
172
157
|
end
|
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def key_provider
|
|
53
|
-
@
|
|
53
|
+
@key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def merge(other_scheme)
|
|
@@ -80,10 +80,14 @@ module ActiveRecord
|
|
|
80
80
|
raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
def
|
|
84
|
-
|
|
83
|
+
def key_provider_from_key
|
|
84
|
+
@key_provider_from_key ||= if @key.present?
|
|
85
|
+
DerivedSecretKeyProvider.new(@key)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
def deterministic_key_provider
|
|
90
|
+
@deterministic_key_provider ||= if @deterministic
|
|
87
91
|
DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
|
|
88
92
|
end
|
|
89
93
|
end
|
data/lib/active_record/enum.rb
CHANGED
|
@@ -167,15 +167,6 @@ module ActiveRecord
|
|
|
167
167
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
-
def load_schema! # :nodoc:
|
|
171
|
-
attributes_to_define_after_schema_loads.each do |name, (cast_type, _default)|
|
|
172
|
-
unless columns_hash.key?(name)
|
|
173
|
-
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
|
174
|
-
raise "Unknown enum attribute '#{name}' for #{self.name}" if Enum::EnumType === cast_type
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
170
|
class EnumType < Type::Value # :nodoc:
|
|
180
171
|
delegate :type, to: :subtype
|
|
181
172
|
|
|
@@ -255,6 +246,12 @@ module ActiveRecord
|
|
|
255
246
|
detect_enum_conflict!(name, "#{name}=")
|
|
256
247
|
|
|
257
248
|
attribute(name, **options) do |subtype|
|
|
249
|
+
if subtype == ActiveModel::Type.default_value
|
|
250
|
+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
|
251
|
+
" backed by a database column or declared with an explicit type" \
|
|
252
|
+
" via `attribute`."
|
|
253
|
+
end
|
|
254
|
+
|
|
258
255
|
subtype = subtype.subtype if EnumType === subtype
|
|
259
256
|
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
|
260
257
|
end
|
data/lib/active_record/errors.rb
CHANGED
|
@@ -318,14 +318,15 @@ module ActiveRecord
|
|
|
318
318
|
class << self
|
|
319
319
|
def db_error(db_name)
|
|
320
320
|
NoDatabaseError.new(<<~MSG)
|
|
321
|
-
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml
|
|
321
|
+
We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
|
|
322
322
|
|
|
323
323
|
To resolve this error:
|
|
324
324
|
|
|
325
|
-
- Did you create the database
|
|
326
|
-
- Has the database name changed? Check your database.yml config has the correct database name.
|
|
325
|
+
- Did you not create the database, or did you delete it? To create the database, run:
|
|
327
326
|
|
|
328
|
-
|
|
327
|
+
bin/rails db:create
|
|
328
|
+
|
|
329
|
+
- Has the database name changed? Verify that config/database.yml contains the correct database name.
|
|
329
330
|
MSG
|
|
330
331
|
end
|
|
331
332
|
end
|
|
@@ -268,6 +268,8 @@ module ActiveRecord
|
|
|
268
268
|
# name: Reginald the Pirate
|
|
269
269
|
# monkey_id: 1
|
|
270
270
|
#
|
|
271
|
+
# <code></code>
|
|
272
|
+
#
|
|
271
273
|
# ### in monkeys.yml
|
|
272
274
|
#
|
|
273
275
|
# george:
|
|
@@ -285,6 +287,8 @@ module ActiveRecord
|
|
|
285
287
|
# name: Reginald the Pirate
|
|
286
288
|
# monkey: george
|
|
287
289
|
#
|
|
290
|
+
# <code></code>
|
|
291
|
+
#
|
|
288
292
|
# ### in monkeys.yml
|
|
289
293
|
#
|
|
290
294
|
# george:
|
|
@@ -306,6 +310,8 @@ module ActiveRecord
|
|
|
306
310
|
#
|
|
307
311
|
# belongs_to :eater, polymorphic: true
|
|
308
312
|
#
|
|
313
|
+
# <code></code>
|
|
314
|
+
#
|
|
309
315
|
# ### in fruits.yml
|
|
310
316
|
#
|
|
311
317
|
# apple:
|
|
@@ -331,6 +337,8 @@ module ActiveRecord
|
|
|
331
337
|
# id: 1
|
|
332
338
|
# name: George the Monkey
|
|
333
339
|
#
|
|
340
|
+
# <code></code>
|
|
341
|
+
#
|
|
334
342
|
# ### in fruits.yml
|
|
335
343
|
#
|
|
336
344
|
# apple:
|
|
@@ -345,6 +353,8 @@ module ActiveRecord
|
|
|
345
353
|
# id: 3
|
|
346
354
|
# name: grape
|
|
347
355
|
#
|
|
356
|
+
# <code></code>
|
|
357
|
+
#
|
|
348
358
|
# ### in fruits_monkeys.yml
|
|
349
359
|
#
|
|
350
360
|
# apple_george:
|
|
@@ -368,6 +378,8 @@ module ActiveRecord
|
|
|
368
378
|
# name: George the Monkey
|
|
369
379
|
# fruits: apple, orange, grape
|
|
370
380
|
#
|
|
381
|
+
# <code></code>
|
|
382
|
+
#
|
|
371
383
|
# ### in fruits.yml
|
|
372
384
|
#
|
|
373
385
|
# apple:
|
|
@@ -467,6 +479,8 @@ module ActiveRecord
|
|
|
467
479
|
# belongs_to :author
|
|
468
480
|
# end
|
|
469
481
|
#
|
|
482
|
+
# <code></code>
|
|
483
|
+
#
|
|
470
484
|
# # books.yml
|
|
471
485
|
# alices_adventure_in_wonderland:
|
|
472
486
|
# author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
|
|
@@ -482,6 +496,8 @@ module ActiveRecord
|
|
|
482
496
|
# belongs_to :book, query_constraints: [:author_id, :book_id]
|
|
483
497
|
# end
|
|
484
498
|
#
|
|
499
|
+
# <code></code>
|
|
500
|
+
#
|
|
485
501
|
# # book_orders.yml
|
|
486
502
|
# alices_adventure_in_wonderland_in_books:
|
|
487
503
|
# author: lewis_carroll
|
|
@@ -47,7 +47,17 @@ module ActiveRecord
|
|
|
47
47
|
|
|
48
48
|
Canceled = Class.new(ActiveRecordError)
|
|
49
49
|
|
|
50
|
+
def self.wrap(result)
|
|
51
|
+
case result
|
|
52
|
+
when self, Complete
|
|
53
|
+
result
|
|
54
|
+
else
|
|
55
|
+
Complete.new(result)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
50
59
|
delegate :empty?, :to_a, to: :result
|
|
60
|
+
delegate_missing_to :result
|
|
51
61
|
|
|
52
62
|
attr_reader :lock_wait
|
|
53
63
|
|
|
@@ -23,8 +23,6 @@ module ActiveRecord
|
|
|
23
23
|
@keys = @inserts.first.keys
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
configure_on_duplicate_update_logic
|
|
27
|
-
|
|
28
26
|
if model.scope_attributes?
|
|
29
27
|
@scope_attributes = model.scope_attributes
|
|
30
28
|
@keys |= @scope_attributes.keys
|
|
@@ -35,8 +33,8 @@ module ActiveRecord
|
|
|
35
33
|
@returning = false if @returning == []
|
|
36
34
|
|
|
37
35
|
@unique_by = find_unique_index_for(@unique_by)
|
|
38
|
-
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
|
|
39
36
|
|
|
37
|
+
configure_on_duplicate_update_logic
|
|
40
38
|
ensure_valid_options_for_connection!
|
|
41
39
|
end
|
|
42
40
|
|
|
@@ -135,6 +133,8 @@ module ActiveRecord
|
|
|
135
133
|
elsif custom_update_sql_provided?
|
|
136
134
|
@update_sql = on_duplicate
|
|
137
135
|
@on_duplicate = :update
|
|
136
|
+
elsif @on_duplicate == :update && updatable_columns.empty?
|
|
137
|
+
@on_duplicate = :skip
|
|
138
138
|
end
|
|
139
139
|
end
|
|
140
140
|
|
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
|
10
10
|
# This is enabled by default. To disable this functionality set
|
|
11
11
|
# `use_metadata_table` to false in your database configuration.
|
|
12
12
|
class InternalMetadata # :nodoc:
|
|
13
|
-
class NullInternalMetadata
|
|
13
|
+
class NullInternalMetadata # :nodoc:
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
attr_reader :connection, :arel_table
|
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
|
14
14
|
# == Usage
|
|
15
15
|
#
|
|
16
16
|
# Active Record supports optimistic locking if the +lock_version+ field is present. Each update to the
|
|
17
|
-
# record increments the +lock_version+
|
|
17
|
+
# record increments the integer column +lock_version+ and the locking facilities ensure that records instantiated twice
|
|
18
18
|
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
|
|
19
19
|
#
|
|
20
20
|
# p1 = Person.find(1)
|
|
@@ -25,7 +25,10 @@ module ActiveRecord
|
|
|
25
25
|
payload = [attributes_for_database, new_record?]
|
|
26
26
|
|
|
27
27
|
cached_associations = self.class.reflect_on_all_associations.select do |reflection|
|
|
28
|
-
association_cached?(reflection.name)
|
|
28
|
+
if association_cached?(reflection.name)
|
|
29
|
+
association = association(reflection.name)
|
|
30
|
+
association.loaded? || association.target.present?
|
|
31
|
+
end
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
unless cached_associations.empty?
|
|
@@ -80,7 +80,7 @@ module ActiveRecord
|
|
|
80
80
|
|
|
81
81
|
def add_cached_associations(record, entry)
|
|
82
82
|
record.class.reflections.each_value do |reflection|
|
|
83
|
-
if record.association_cached?(reflection.name)
|
|
83
|
+
if record.association_cached?(reflection.name) && record.association(reflection.name).loaded?
|
|
84
84
|
entry << reflection.name << encode(record.association(reflection.name).target)
|
|
85
85
|
end
|
|
86
86
|
end
|
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
|
24
24
|
# To use the DatabaseSelector in your application with default settings,
|
|
25
25
|
# run the provided generator.
|
|
26
26
|
#
|
|
27
|
-
# bin/rails g active_record:multi_db
|
|
27
|
+
# $ bin/rails g active_record:multi_db
|
|
28
28
|
#
|
|
29
29
|
# This will create a file named +config/initializers/multi_db.rb+ with the
|
|
30
30
|
# following contents:
|
|
@@ -61,8 +61,10 @@ module ActiveRecord
|
|
|
61
61
|
column_name.is_a?(String) && /\W/.match?(column_name)
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
+
|
|
64
65
|
module TableDefinition
|
|
65
66
|
include LegacyIndexName
|
|
67
|
+
|
|
66
68
|
def column(name, type, **options)
|
|
67
69
|
options[:_skip_validate_options] = true
|
|
68
70
|
super
|
|
@@ -78,6 +80,11 @@ module ActiveRecord
|
|
|
78
80
|
super
|
|
79
81
|
end
|
|
80
82
|
|
|
83
|
+
def references(*args, **options)
|
|
84
|
+
options[:_skip_validate_options] = true
|
|
85
|
+
super
|
|
86
|
+
end
|
|
87
|
+
|
|
81
88
|
private
|
|
82
89
|
def raise_on_if_exist_options(options)
|
|
83
90
|
end
|
|
@@ -95,6 +102,12 @@ module ActiveRecord
|
|
|
95
102
|
super
|
|
96
103
|
end
|
|
97
104
|
|
|
105
|
+
def add_reference(table_name, ref_name, **options)
|
|
106
|
+
options[:_skip_validate_options] = true
|
|
107
|
+
super
|
|
108
|
+
end
|
|
109
|
+
alias :add_belongs_to :add_reference
|
|
110
|
+
|
|
98
111
|
def create_table(table_name, **options)
|
|
99
112
|
options[:_uses_legacy_table_name] = true
|
|
100
113
|
options[:_skip_validate_options] = true
|
|
@@ -104,6 +117,7 @@ module ActiveRecord
|
|
|
104
117
|
|
|
105
118
|
def rename_table(table_name, new_name, **options)
|
|
106
119
|
options[:_uses_legacy_table_name] = true
|
|
120
|
+
options[:_uses_legacy_index_name] = true
|
|
107
121
|
super
|
|
108
122
|
end
|
|
109
123
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class PendingMigrationConnection # :nodoc:
|
|
5
|
+
def self.establish_temporary_connection(db_config, &block)
|
|
6
|
+
pool = ActiveRecord::Base.connection_handler.establish_connection(db_config, owner_name: self)
|
|
7
|
+
|
|
8
|
+
yield pool.connection
|
|
9
|
+
ensure
|
|
10
|
+
ActiveRecord::Base.connection_handler.remove_connection_pool(self.name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.primary_class?
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.current_preventing_writes
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -7,6 +7,7 @@ require "active_support/core_ext/array/access"
|
|
|
7
7
|
require "active_support/core_ext/enumerable"
|
|
8
8
|
require "active_support/core_ext/module/attribute_accessors"
|
|
9
9
|
require "active_support/actionable_error"
|
|
10
|
+
require "active_record/migration/pending_migration_connection"
|
|
10
11
|
|
|
11
12
|
module ActiveRecord
|
|
12
13
|
class MigrationError < ActiveRecordError # :nodoc:
|
|
@@ -370,7 +371,8 @@ module ActiveRecord
|
|
|
370
371
|
# The \Rails package has several tools to help create and apply migrations.
|
|
371
372
|
#
|
|
372
373
|
# To generate a new migration, you can use
|
|
373
|
-
#
|
|
374
|
+
#
|
|
375
|
+
# $ bin/rails generate migration MyNewMigration
|
|
374
376
|
#
|
|
375
377
|
# where MyNewMigration is the name of your migration. The generator will
|
|
376
378
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
|
@@ -379,7 +381,7 @@ module ActiveRecord
|
|
|
379
381
|
#
|
|
380
382
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
|
381
383
|
#
|
|
382
|
-
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
|
384
|
+
# $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
|
383
385
|
#
|
|
384
386
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
|
385
387
|
# class AddFieldnameToTablename < ActiveRecord::Migration[7.1]
|
|
@@ -768,9 +770,11 @@ module ActiveRecord
|
|
|
768
770
|
def pending_migrations
|
|
769
771
|
pending_migrations = []
|
|
770
772
|
|
|
771
|
-
ActiveRecord::
|
|
772
|
-
|
|
773
|
-
|
|
773
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
|
774
|
+
ActiveRecord::PendingMigrationConnection.establish_temporary_connection(db_config) do |conn|
|
|
775
|
+
if pending = conn.migration_context.open.pending_migrations
|
|
776
|
+
pending_migrations << pending
|
|
777
|
+
end
|
|
774
778
|
end
|
|
775
779
|
end
|
|
776
780
|
|
|
@@ -284,8 +284,10 @@ module ActiveRecord
|
|
|
284
284
|
|
|
285
285
|
# Computes the table name, (re)sets it internally, and returns it.
|
|
286
286
|
def reset_table_name # :nodoc:
|
|
287
|
-
self.table_name = if
|
|
288
|
-
|
|
287
|
+
self.table_name = if self == Base
|
|
288
|
+
nil
|
|
289
|
+
elsif abstract_class?
|
|
290
|
+
superclass.table_name
|
|
289
291
|
elsif superclass.abstract_class?
|
|
290
292
|
superclass.table_name || compute_table_name
|
|
291
293
|
else
|
|
@@ -430,8 +432,12 @@ module ActiveRecord
|
|
|
430
432
|
end
|
|
431
433
|
|
|
432
434
|
def _returning_columns_for_insert # :nodoc:
|
|
433
|
-
@_returning_columns_for_insert ||=
|
|
434
|
-
|
|
435
|
+
@_returning_columns_for_insert ||= begin
|
|
436
|
+
auto_populated_columns = columns.filter_map do |c|
|
|
437
|
+
c.name if connection.return_value_after_insert?(c)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
auto_populated_columns.empty? ? Array(primary_key) : auto_populated_columns
|
|
435
441
|
end
|
|
436
442
|
end
|
|
437
443
|
|
|
@@ -467,7 +473,7 @@ module ActiveRecord
|
|
|
467
473
|
end
|
|
468
474
|
|
|
469
475
|
# Returns the column object for the named attribute.
|
|
470
|
-
# Returns an
|
|
476
|
+
# Returns an ActiveRecord::ConnectionAdapters::NullColumn if the
|
|
471
477
|
# named attribute does not exist.
|
|
472
478
|
#
|
|
473
479
|
# class Person < ActiveRecord::Base
|
|
@@ -627,8 +633,7 @@ module ActiveRecord
|
|
|
627
633
|
)
|
|
628
634
|
alias_attribute :id_value, :id if name == "id"
|
|
629
635
|
end
|
|
630
|
-
|
|
631
|
-
super
|
|
636
|
+
_default_attributes # Precompute to cache DB-dependent attribute types
|
|
632
637
|
end
|
|
633
638
|
|
|
634
639
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|