activerecord 7.1.5.2 → 8.1.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
|
13
13
|
scope: [:kind, :name]
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
attr_accessor :_new_record_before_last_commit # :nodoc:
|
|
16
|
+
attr_accessor :_new_record_before_last_commit, :_last_transaction_return_status # :nodoc:
|
|
17
17
|
|
|
18
18
|
# = Active Record \Transactions
|
|
19
19
|
#
|
|
@@ -188,6 +188,27 @@ module ActiveRecord
|
|
|
188
188
|
# #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
|
|
189
189
|
# within a transaction could trigger the cache to be regenerated before the database is updated.
|
|
190
190
|
#
|
|
191
|
+
# ==== NOTE: Callbacks are deduplicated per callback by filter.
|
|
192
|
+
#
|
|
193
|
+
# Trying to define multiple callbacks with the same filter will result in a single callback being run.
|
|
194
|
+
#
|
|
195
|
+
# For example:
|
|
196
|
+
#
|
|
197
|
+
# after_commit :do_something
|
|
198
|
+
# after_commit :do_something # only the last one will be called
|
|
199
|
+
#
|
|
200
|
+
# This applies to all variations of <tt>after_*_commit</tt> callbacks as well.
|
|
201
|
+
#
|
|
202
|
+
# after_commit :do_something
|
|
203
|
+
# after_create_commit :do_something
|
|
204
|
+
# after_save_commit :do_something
|
|
205
|
+
#
|
|
206
|
+
# It is recommended to use the +on:+ option to specify when the callback should be run.
|
|
207
|
+
#
|
|
208
|
+
# after_commit :do_something, on: [:create, :update]
|
|
209
|
+
#
|
|
210
|
+
# This is equivalent to using +after_create_commit+ and +after_update_commit+, but will not be deduplicated.
|
|
211
|
+
#
|
|
191
212
|
# === Caveats
|
|
192
213
|
#
|
|
193
214
|
# If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
|
|
@@ -198,18 +219,50 @@ module ActiveRecord
|
|
|
198
219
|
# database error will occur because the savepoint has already been
|
|
199
220
|
# automatically released. The following example demonstrates the problem:
|
|
200
221
|
#
|
|
201
|
-
# Model.
|
|
202
|
-
# Model.
|
|
203
|
-
# Model.
|
|
204
|
-
# end
|
|
205
|
-
#
|
|
206
|
-
# end
|
|
222
|
+
# Model.transaction do # BEGIN
|
|
223
|
+
# Model.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
|
224
|
+
# Model.lease_connection.create_table(...) # active_record_1 now automatically released
|
|
225
|
+
# end # RELEASE SAVEPOINT active_record_1
|
|
226
|
+
# end # ^^^^ BOOM! database error!
|
|
207
227
|
#
|
|
208
228
|
# Note that "TRUNCATE" is also a MySQL DDL statement!
|
|
209
229
|
module ClassMethods
|
|
210
230
|
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
|
|
211
231
|
def transaction(**options, &block)
|
|
212
|
-
connection
|
|
232
|
+
with_connection do |connection|
|
|
233
|
+
connection.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
|
|
234
|
+
connection.transaction(**options, &block)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Makes all transactions the current pool use the isolation level initiated within the block.
|
|
240
|
+
def with_pool_transaction_isolation_level(isolation_level, &block)
|
|
241
|
+
if current_transaction.open?
|
|
242
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set default isolation level while transaction is open"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
old_level = connection_pool.pool_transaction_isolation_level
|
|
246
|
+
connection_pool.pool_transaction_isolation_level = isolation_level
|
|
247
|
+
yield
|
|
248
|
+
ensure
|
|
249
|
+
connection_pool.pool_transaction_isolation_level = old_level
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Returns the default isolation level for the connection pool, set earlier by #with_pool_transaction_isolation_level.
|
|
253
|
+
def pool_transaction_isolation_level
|
|
254
|
+
connection_pool.pool_transaction_isolation_level
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Returns a representation of the current transaction state,
|
|
258
|
+
# which can be a top level transaction, a savepoint, or the absence of a transaction.
|
|
259
|
+
#
|
|
260
|
+
# An object is always returned, whether or not a transaction is currently active.
|
|
261
|
+
# To check if a transaction was opened, use <tt>current_transaction.open?</tt>.
|
|
262
|
+
#
|
|
263
|
+
# See the ActiveRecord::Transaction documentation for detailed behavior.
|
|
264
|
+
def current_transaction
|
|
265
|
+
connection_pool.active_connection&.current_transaction&.user_transaction || Transaction::NULL_TRANSACTION
|
|
213
266
|
end
|
|
214
267
|
|
|
215
268
|
def before_commit(*args, &block) # :nodoc:
|
|
@@ -266,6 +319,25 @@ module ActiveRecord
|
|
|
266
319
|
set_callback(:rollback, :after, *args, &block)
|
|
267
320
|
end
|
|
268
321
|
|
|
322
|
+
# Similar to ActiveSupport::Callbacks::ClassMethods#set_callback, but with
|
|
323
|
+
# support for options available on #after_commit and #after_rollback callbacks.
|
|
324
|
+
def set_callback(name, *filter_list, &block)
|
|
325
|
+
options = filter_list.extract_options!
|
|
326
|
+
filter_list << options
|
|
327
|
+
|
|
328
|
+
if name.in?([:commit, :rollback]) && options[:on]
|
|
329
|
+
fire_on = Array(options[:on])
|
|
330
|
+
assert_valid_transaction_action(fire_on)
|
|
331
|
+
options[:if] = [
|
|
332
|
+
-> { transaction_include_any_action?(fire_on) },
|
|
333
|
+
*options[:if]
|
|
334
|
+
]
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
super(name, *filter_list, &block)
|
|
339
|
+
end
|
|
340
|
+
|
|
269
341
|
private
|
|
270
342
|
def prepend_option
|
|
271
343
|
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
@@ -354,18 +426,22 @@ module ActiveRecord
|
|
|
354
426
|
# This method is available within the context of an ActiveRecord::Base
|
|
355
427
|
# instance.
|
|
356
428
|
def with_transaction_returning_status
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
429
|
+
self.class.with_connection do |connection|
|
|
430
|
+
connection.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
|
|
431
|
+
status = nil
|
|
432
|
+
ensure_finalize = !connection.transaction_open?
|
|
360
433
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
434
|
+
connection.transaction do
|
|
435
|
+
add_to_transaction(ensure_finalize || has_transactional_callbacks?)
|
|
436
|
+
remember_transaction_record_state
|
|
364
437
|
|
|
365
|
-
|
|
366
|
-
|
|
438
|
+
status = yield
|
|
439
|
+
raise ActiveRecord::Rollback unless status
|
|
440
|
+
end
|
|
441
|
+
@_last_transaction_return_status = status
|
|
442
|
+
status
|
|
443
|
+
end
|
|
367
444
|
end
|
|
368
|
-
status
|
|
369
445
|
end
|
|
370
446
|
|
|
371
447
|
def trigger_transactional_callbacks? # :nodoc:
|
|
@@ -379,6 +455,7 @@ module ActiveRecord
|
|
|
379
455
|
def init_internals
|
|
380
456
|
super
|
|
381
457
|
@_start_transaction_state = nil
|
|
458
|
+
@_last_transaction_return_status = nil
|
|
382
459
|
@_committed_already_called = nil
|
|
383
460
|
@_new_record_before_last_commit = nil
|
|
384
461
|
end
|
|
@@ -457,7 +534,9 @@ module ActiveRecord
|
|
|
457
534
|
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
|
458
535
|
# callbacks can be called.
|
|
459
536
|
def add_to_transaction(ensure_finalize = true)
|
|
460
|
-
self.class.connection
|
|
537
|
+
self.class.with_connection do |connection|
|
|
538
|
+
connection.add_transaction_record(self, ensure_finalize)
|
|
539
|
+
end
|
|
461
540
|
end
|
|
462
541
|
|
|
463
542
|
def has_transactional_callbacks?
|
|
@@ -26,6 +26,7 @@ module ActiveRecord
|
|
|
26
26
|
if block
|
|
27
27
|
@mapping[key] = block
|
|
28
28
|
else
|
|
29
|
+
value.freeze
|
|
29
30
|
@mapping[key] = proc { value }
|
|
30
31
|
end
|
|
31
32
|
@cache.clear
|
|
@@ -50,7 +51,7 @@ module ActiveRecord
|
|
|
50
51
|
|
|
51
52
|
private
|
|
52
53
|
def perform_fetch(type, *args, &block)
|
|
53
|
-
@mapping.fetch(type, block).call(type, *args)
|
|
54
|
+
@mapping.fetch(type, block).call(type, *args).freeze
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/json"
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
6
|
module Type
|
|
5
7
|
class Json < ActiveModel::Type::Value
|
|
@@ -11,11 +13,22 @@ module ActiveRecord
|
|
|
11
13
|
|
|
12
14
|
def deserialize(value)
|
|
13
15
|
return value unless value.is_a?(::String)
|
|
14
|
-
|
|
16
|
+
begin
|
|
17
|
+
ActiveSupport::JSON.decode(value)
|
|
18
|
+
rescue JSON::ParserError => e
|
|
19
|
+
# NOTE: This may hide json with duplicate keys. We don't really want to just ignore it
|
|
20
|
+
# but it's the best we can do in order to still allow updating columns that somehow already
|
|
21
|
+
# contain invalid json from some other source.
|
|
22
|
+
# See https://github.com/rails/rails/pull/55536
|
|
23
|
+
ActiveSupport.error_reporter.report(e, source: "application.active_record")
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
15
26
|
end
|
|
16
27
|
|
|
28
|
+
JSON_ENCODER = ActiveSupport::JSON::Encoding.json_encoder.new(escape: false)
|
|
29
|
+
|
|
17
30
|
def serialize(value)
|
|
18
|
-
|
|
31
|
+
JSON_ENCODER.encode(value) unless value.nil?
|
|
19
32
|
end
|
|
20
33
|
|
|
21
34
|
def changed_in_place?(raw_old_value, new_value)
|
|
@@ -9,9 +9,10 @@ module ActiveRecord
|
|
|
9
9
|
|
|
10
10
|
attr_reader :subtype, :coder
|
|
11
11
|
|
|
12
|
-
def initialize(subtype, coder)
|
|
12
|
+
def initialize(subtype, coder, comparable: false)
|
|
13
13
|
@subtype = subtype
|
|
14
14
|
@coder = coder
|
|
15
|
+
@comparable = comparable
|
|
15
16
|
super(subtype)
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -30,15 +31,19 @@ module ActiveRecord
|
|
|
30
31
|
end
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
Kernel.instance_method(:inspect).bind_call(self)
|
|
35
|
-
end
|
|
34
|
+
define_method(:inspect, Kernel.instance_method(:inspect))
|
|
36
35
|
|
|
37
36
|
def changed_in_place?(raw_old_value, value)
|
|
38
37
|
return false if value.nil?
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
|
|
39
|
+
if @comparable
|
|
40
|
+
old_value = deserialize(raw_old_value)
|
|
41
|
+
old_value != value
|
|
42
|
+
else
|
|
43
|
+
raw_new_value = encoded(value)
|
|
44
|
+
raw_old_value.nil? != raw_new_value.nil? ||
|
|
45
|
+
subtype.changed_in_place?(raw_old_value, raw_new_value)
|
|
46
|
+
end
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def accessor
|
|
@@ -14,18 +14,19 @@ module ActiveRecord
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def type_for_attribute(attr_name)
|
|
17
|
-
schema_cache =
|
|
17
|
+
schema_cache = @klass.schema_cache
|
|
18
18
|
|
|
19
19
|
if schema_cache.data_source_exists?(table_name)
|
|
20
20
|
column = schema_cache.columns_hash(table_name)[attr_name.to_s]
|
|
21
|
-
|
|
21
|
+
if column
|
|
22
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
23
|
+
type = column.fetch_cast_type(@klass.lease_connection)
|
|
24
|
+
end
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
type || Type.default_value
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
delegate :connection, to: :@klass, private: true
|
|
28
|
-
|
|
29
30
|
private
|
|
30
31
|
attr_reader :table_name
|
|
31
32
|
end
|
|
@@ -4,14 +4,20 @@ module ActiveRecord
|
|
|
4
4
|
module Validations
|
|
5
5
|
class AssociatedValidator < ActiveModel::EachValidator # :nodoc:
|
|
6
6
|
def validate_each(record, attribute, value)
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
context = record_validation_context_for_association(record)
|
|
8
|
+
|
|
9
|
+
if Array(value).reject { |association| valid_object?(association, context) }.any?
|
|
10
|
+
record.errors.add(attribute, :invalid, **options, value: value)
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
private
|
|
13
|
-
def valid_object?(record)
|
|
14
|
-
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
|
|
15
|
+
def valid_object?(record, context)
|
|
16
|
+
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?(context)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def record_validation_context_for_association(record)
|
|
20
|
+
record.custom_validation_context? ? record.validation_context : nil
|
|
15
21
|
end
|
|
16
22
|
end
|
|
17
23
|
|
|
@@ -14,6 +14,7 @@ module ActiveRecord
|
|
|
14
14
|
end
|
|
15
15
|
super
|
|
16
16
|
@klass = options[:class]
|
|
17
|
+
@klass = @klass.superclass if @klass.singleton_class?
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def validate_each(record, attribute, value)
|
|
@@ -53,17 +54,17 @@ module ActiveRecord
|
|
|
53
54
|
private
|
|
54
55
|
# The check for an existing value should be run from a class that
|
|
55
56
|
# isn't abstract. This means working down from the current class
|
|
56
|
-
# (self), to the first non-abstract class.
|
|
57
|
-
# their subclasses, we have to build the hierarchy between self and
|
|
58
|
-
# the record's class.
|
|
57
|
+
# (self), to the first non-abstract class.
|
|
59
58
|
def find_finder_class_for(record)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
current_class = record.class
|
|
60
|
+
found_class = nil
|
|
61
|
+
loop do
|
|
62
|
+
found_class = current_class unless current_class.abstract_class?
|
|
63
|
+
break if current_class == @klass
|
|
64
|
+
current_class = current_class.superclass
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
found_class
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def validation_needed?(klass, record, attribute)
|
|
@@ -84,7 +85,7 @@ module ActiveRecord
|
|
|
84
85
|
attributes = scope + [attr]
|
|
85
86
|
attributes = resolve_attributes(record, attributes)
|
|
86
87
|
|
|
87
|
-
klass.
|
|
88
|
+
klass.schema_cache.indexes(klass.table_name).any? do |index|
|
|
88
89
|
index.unique &&
|
|
89
90
|
index.where.nil? &&
|
|
90
91
|
(Array(index.columns) - attributes).empty?
|
|
@@ -110,16 +111,20 @@ module ActiveRecord
|
|
|
110
111
|
|
|
111
112
|
def build_relation(klass, attribute, value)
|
|
112
113
|
relation = klass.unscoped
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
# TODO: Add case-sensitive / case-insensitive operators to Arel
|
|
115
|
+
# to no longer need to checkout a connection here.
|
|
116
|
+
comparison = klass.with_connection do |connection|
|
|
117
|
+
relation.bind_attribute(attribute, value) do |attr, bind|
|
|
118
|
+
return relation.none! if bind.unboundable?
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
if !options.key?(:case_sensitive) || bind.nil?
|
|
121
|
+
connection.default_uniqueness_comparison(attr, bind)
|
|
122
|
+
elsif options[:case_sensitive]
|
|
123
|
+
connection.case_sensitive_comparison(attr, bind)
|
|
124
|
+
else
|
|
125
|
+
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
|
|
126
|
+
connection.case_insensitive_comparison(attr, bind)
|
|
127
|
+
end
|
|
123
128
|
end
|
|
124
129
|
end
|
|
125
130
|
|
|
@@ -39,7 +39,6 @@ module ActiveRecord
|
|
|
39
39
|
# {new_record?}[rdoc-ref:Persistence#new_record?].
|
|
40
40
|
module Validations
|
|
41
41
|
extend ActiveSupport::Concern
|
|
42
|
-
include ActiveModel::Validations
|
|
43
42
|
|
|
44
43
|
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
|
45
44
|
# The validation context can be changed by passing <tt>context: context</tt>.
|
|
@@ -75,6 +74,10 @@ module ActiveRecord
|
|
|
75
74
|
|
|
76
75
|
alias_method :validate, :valid?
|
|
77
76
|
|
|
77
|
+
def custom_validation_context? # :nodoc:
|
|
78
|
+
validation_context && [:create, :update].exclude?(validation_context)
|
|
79
|
+
end
|
|
80
|
+
|
|
78
81
|
private
|
|
79
82
|
def default_validation_context
|
|
80
83
|
new_record? ? :create : :update
|