activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- 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 +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- 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/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -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/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -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 +92 -295
- 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/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- 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 +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- 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 +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- 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 +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- 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 +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -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/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -293,6 +293,11 @@ module ActiveRecord
|
|
293
293
|
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
294
294
|
validate_create_table_options!(options)
|
295
295
|
validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
|
296
|
+
|
297
|
+
if force && options.key?(:if_not_exists)
|
298
|
+
raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
|
299
|
+
end
|
300
|
+
|
296
301
|
td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
|
297
302
|
|
298
303
|
if force
|
@@ -340,6 +345,15 @@ module ActiveRecord
|
|
340
345
|
# # Creates a table called 'assemblies_parts' with no id.
|
341
346
|
# create_join_table(:assemblies, :parts)
|
342
347
|
#
|
348
|
+
# # Creates a table called 'paper_boxes_papers' with no id.
|
349
|
+
# create_join_table('papers', 'paper_boxes')
|
350
|
+
#
|
351
|
+
# A duplicate prefix is combined into a single prefix. This is useful for
|
352
|
+
# namespaced models like Music::Artist and Music::Record:
|
353
|
+
#
|
354
|
+
# # Creates a table called 'music_artists_records' with no id.
|
355
|
+
# create_join_table('music_artists', 'music_records')
|
356
|
+
#
|
343
357
|
# You can pass an +options+ hash which can include the following keys:
|
344
358
|
# [<tt>:table_name</tt>]
|
345
359
|
# Sets the table name, overriding the default.
|
@@ -511,7 +525,7 @@ module ActiveRecord
|
|
511
525
|
raise NotImplementedError, "rename_table is not implemented"
|
512
526
|
end
|
513
527
|
|
514
|
-
# Drops a table from the database.
|
528
|
+
# Drops a table or tables from the database.
|
515
529
|
#
|
516
530
|
# [<tt>:force</tt>]
|
517
531
|
# Set to +:cascade+ to drop dependent objects as well.
|
@@ -522,10 +536,12 @@ module ActiveRecord
|
|
522
536
|
#
|
523
537
|
# Although this command ignores most +options+ and the block if one is given,
|
524
538
|
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
|
525
|
-
# In that case, +options+ and the block will be used by #create_table.
|
526
|
-
def drop_table(
|
527
|
-
|
528
|
-
|
539
|
+
# In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
|
540
|
+
def drop_table(*table_names, **options)
|
541
|
+
table_names.each do |table_name|
|
542
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
543
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
544
|
+
end
|
529
545
|
end
|
530
546
|
|
531
547
|
# Add a new +type+ column named +column_name+ to +table_name+.
|
@@ -677,7 +693,7 @@ module ActiveRecord
|
|
677
693
|
#
|
678
694
|
# If the options provided include an +if_exists+ key, it will be used to check if the
|
679
695
|
# column does not exist. This will silently ignore the migration rather than raising
|
680
|
-
# if the column was already
|
696
|
+
# if the column was already removed.
|
681
697
|
#
|
682
698
|
# remove_column(:suppliers, :qualification, if_exists: true)
|
683
699
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -839,6 +855,16 @@ module ActiveRecord
|
|
839
855
|
#
|
840
856
|
# Note: only supported by PostgreSQL.
|
841
857
|
#
|
858
|
+
# ====== Creating an index where NULLs are treated equally
|
859
|
+
#
|
860
|
+
# add_index(:people, :last_name, nulls_not_distinct: true)
|
861
|
+
#
|
862
|
+
# generates:
|
863
|
+
#
|
864
|
+
# CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
|
865
|
+
#
|
866
|
+
# Note: only supported by PostgreSQL version 15.0.0 and greater.
|
867
|
+
#
|
842
868
|
# ====== Creating an index with a specific method
|
843
869
|
#
|
844
870
|
# add_index(:developers, :name, using: 'btree')
|
@@ -876,9 +902,12 @@ module ActiveRecord
|
|
876
902
|
# ====== Creating an index with a specific algorithm
|
877
903
|
#
|
878
904
|
# add_index(:developers, :name, algorithm: :concurrently)
|
879
|
-
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
|
905
|
+
# # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
|
880
906
|
#
|
881
|
-
#
|
907
|
+
# add_index(:developers, :name, algorithm: :inplace)
|
908
|
+
# # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
|
909
|
+
#
|
910
|
+
# Note: only supported by PostgreSQL and MySQL.
|
882
911
|
#
|
883
912
|
# Concurrently adding an index is not supported in a transaction.
|
884
913
|
#
|
@@ -1305,7 +1334,6 @@ module ActiveRecord
|
|
1305
1334
|
execute schema_creation.accept(at)
|
1306
1335
|
end
|
1307
1336
|
|
1308
|
-
|
1309
1337
|
# Checks to see if a check constraint exists on a table for a given check constraint definition.
|
1310
1338
|
#
|
1311
1339
|
# check_constraint_exists?(:products, name: "price_check")
|
@@ -1317,8 +1345,15 @@ module ActiveRecord
|
|
1317
1345
|
check_constraint_for(table_name, **options).present?
|
1318
1346
|
end
|
1319
1347
|
|
1348
|
+
def remove_constraint(table_name, constraint_name) # :nodoc:
|
1349
|
+
at = create_alter_table(table_name)
|
1350
|
+
at.drop_constraint(constraint_name)
|
1351
|
+
|
1352
|
+
execute schema_creation.accept(at)
|
1353
|
+
end
|
1354
|
+
|
1320
1355
|
def dump_schema_information # :nodoc:
|
1321
|
-
versions = schema_migration.versions
|
1356
|
+
versions = pool.schema_migration.versions
|
1322
1357
|
insert_versions_sql(versions) if versions.any?
|
1323
1358
|
end
|
1324
1359
|
|
@@ -1328,8 +1363,9 @@ module ActiveRecord
|
|
1328
1363
|
|
1329
1364
|
def assume_migrated_upto_version(version)
|
1330
1365
|
version = version.to_i
|
1331
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1366
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1332
1367
|
|
1368
|
+
migration_context = pool.migration_context
|
1333
1369
|
migrated = migration_context.get_all_versions
|
1334
1370
|
versions = migration_context.migrations.map(&:version)
|
1335
1371
|
|
@@ -1839,7 +1875,7 @@ module ActiveRecord
|
|
1839
1875
|
end
|
1840
1876
|
|
1841
1877
|
def insert_versions_sql(versions)
|
1842
|
-
sm_table = quote_table_name(schema_migration.table_name)
|
1878
|
+
sm_table = quote_table_name(pool.schema_migration.table_name)
|
1843
1879
|
|
1844
1880
|
if versions.is_a?(Array)
|
1845
1881
|
sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
# = Active Record Connection Adapters Transaction State
|
@@ -89,7 +91,9 @@ module ActiveRecord
|
|
89
91
|
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
90
92
|
@started = true
|
91
93
|
|
92
|
-
|
94
|
+
ActiveSupport::Notifications.instrument("start_transaction.active_record", @base_payload)
|
95
|
+
|
96
|
+
@payload = @base_payload.dup # We dup because the payload for a given event is mutated later to add the outcome.
|
93
97
|
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
94
98
|
@handle.start
|
95
99
|
end
|
@@ -104,7 +108,6 @@ module ActiveRecord
|
|
104
108
|
end
|
105
109
|
|
106
110
|
class NullTransaction # :nodoc:
|
107
|
-
def initialize; end
|
108
111
|
def state; end
|
109
112
|
def closed?; true; end
|
110
113
|
def open?; false; end
|
@@ -115,17 +118,43 @@ module ActiveRecord
|
|
115
118
|
def dirty!; end
|
116
119
|
def invalidated?; false; end
|
117
120
|
def invalidate!; end
|
121
|
+
def materialized?; false; end
|
122
|
+
def before_commit; yield; end
|
123
|
+
def after_commit; yield; end
|
124
|
+
def after_rollback; end
|
125
|
+
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
118
126
|
end
|
119
127
|
|
120
128
|
class Transaction # :nodoc:
|
121
|
-
|
129
|
+
class Callback # :nodoc:
|
130
|
+
def initialize(event, callback)
|
131
|
+
@event = event
|
132
|
+
@callback = callback
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_commit
|
136
|
+
@callback.call if @event == :before_commit
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_commit
|
140
|
+
@callback.call if @event == :after_commit
|
141
|
+
end
|
142
|
+
|
143
|
+
def after_rollback
|
144
|
+
@callback.call if @event == :after_rollback
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
attr_reader :connection, :state, :savepoint_name, :isolation_level, :user_transaction
|
122
149
|
attr_accessor :written
|
123
150
|
|
124
151
|
delegate :invalidate!, :invalidated?, to: :@state
|
125
152
|
|
126
153
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
154
|
+
super()
|
127
155
|
@connection = connection
|
128
156
|
@state = TransactionState.new
|
157
|
+
@callbacks = nil
|
129
158
|
@records = nil
|
130
159
|
@isolation_level = isolation
|
131
160
|
@materialized = false
|
@@ -133,7 +162,8 @@ module ActiveRecord
|
|
133
162
|
@run_commit_callbacks = run_commit_callbacks
|
134
163
|
@lazy_enrollment_records = nil
|
135
164
|
@dirty = false
|
136
|
-
@
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
137
167
|
end
|
138
168
|
|
139
169
|
def dirty!
|
@@ -144,6 +174,14 @@ module ActiveRecord
|
|
144
174
|
@dirty
|
145
175
|
end
|
146
176
|
|
177
|
+
def open?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed?
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
147
185
|
def add_record(record, ensure_finalize = true)
|
148
186
|
@records ||= []
|
149
187
|
if ensure_finalize
|
@@ -154,6 +192,30 @@ module ActiveRecord
|
|
154
192
|
end
|
155
193
|
end
|
156
194
|
|
195
|
+
def before_commit(&block)
|
196
|
+
if @state.finalized?
|
197
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
198
|
+
end
|
199
|
+
|
200
|
+
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
201
|
+
end
|
202
|
+
|
203
|
+
def after_commit(&block)
|
204
|
+
if @state.finalized?
|
205
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
206
|
+
end
|
207
|
+
|
208
|
+
(@callbacks ||= []) << Callback.new(:after_commit, block)
|
209
|
+
end
|
210
|
+
|
211
|
+
def after_rollback(&block)
|
212
|
+
if @state.finalized?
|
213
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
214
|
+
end
|
215
|
+
|
216
|
+
(@callbacks ||= []) << Callback.new(:after_rollback, block)
|
217
|
+
end
|
218
|
+
|
157
219
|
def records
|
158
220
|
if @lazy_enrollment_records
|
159
221
|
@records.concat @lazy_enrollment_records.values
|
@@ -190,66 +252,85 @@ module ActiveRecord
|
|
190
252
|
end
|
191
253
|
|
192
254
|
def rollback_records
|
193
|
-
|
194
|
-
|
195
|
-
|
255
|
+
if records
|
256
|
+
begin
|
257
|
+
ite = unique_records
|
196
258
|
|
197
|
-
|
259
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
198
260
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
261
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
262
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
263
|
+
end
|
264
|
+
ensure
|
265
|
+
ite&.each do |i|
|
266
|
+
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
|
267
|
+
end
|
268
|
+
end
|
205
269
|
end
|
270
|
+
|
271
|
+
@callbacks&.each(&:after_rollback)
|
206
272
|
end
|
207
273
|
|
208
274
|
def before_commit_records
|
209
|
-
return unless records
|
210
|
-
|
211
275
|
if @run_commit_callbacks
|
212
|
-
if
|
213
|
-
|
276
|
+
if records
|
277
|
+
if ActiveRecord.before_committed_on_all_records
|
278
|
+
ite = unique_records
|
214
279
|
|
215
|
-
|
216
|
-
|
217
|
-
|
280
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
281
|
+
candidates[record] = record
|
282
|
+
end
|
218
283
|
|
219
|
-
|
220
|
-
|
284
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
285
|
+
record.before_committed! if should_run_callbacks
|
286
|
+
end
|
287
|
+
else
|
288
|
+
records.uniq.each(&:before_committed!)
|
221
289
|
end
|
222
|
-
else
|
223
|
-
records.uniq.each(&:before_committed!)
|
224
290
|
end
|
291
|
+
|
292
|
+
@callbacks&.each(&:before_commit)
|
225
293
|
end
|
294
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
295
|
+
# remaining callbacks to the parent transaction
|
226
296
|
end
|
227
297
|
|
228
298
|
def commit_records
|
229
|
-
|
230
|
-
|
231
|
-
|
299
|
+
if records
|
300
|
+
begin
|
301
|
+
ite = unique_records
|
232
302
|
|
233
|
-
|
234
|
-
|
303
|
+
if @run_commit_callbacks
|
304
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
235
305
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
306
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
307
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
308
|
+
end
|
309
|
+
else
|
310
|
+
while record = ite.shift
|
311
|
+
# if not running callbacks, only adds the record to the parent transaction
|
312
|
+
connection.add_transaction_record(record)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
ensure
|
316
|
+
ite&.each { |i| i.committed!(should_run_callbacks: false) }
|
243
317
|
end
|
244
318
|
end
|
245
|
-
|
246
|
-
|
319
|
+
|
320
|
+
if @run_commit_callbacks
|
321
|
+
@callbacks&.each(&:after_commit)
|
322
|
+
elsif @callbacks
|
323
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
324
|
+
end
|
247
325
|
end
|
248
326
|
|
249
327
|
def full_rollback?; true; end
|
250
328
|
def joinable?; @joinable; end
|
251
|
-
|
252
|
-
|
329
|
+
|
330
|
+
protected
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
333
|
+
end
|
253
334
|
|
254
335
|
private
|
255
336
|
def unique_records
|
@@ -349,7 +430,7 @@ module ActiveRecord
|
|
349
430
|
|
350
431
|
def rollback
|
351
432
|
unless @state.invalidated?
|
352
|
-
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
433
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
353
434
|
end
|
354
435
|
@state.rollback!
|
355
436
|
@instrumenter.finish(:rollback) if materialized?
|
@@ -367,10 +448,14 @@ module ActiveRecord
|
|
367
448
|
# = Active Record Real \Transaction
|
368
449
|
class RealTransaction < Transaction
|
369
450
|
def materialize!
|
370
|
-
if
|
371
|
-
|
451
|
+
if joinable?
|
452
|
+
if isolation_level
|
453
|
+
connection.begin_isolated_db_transaction(isolation_level)
|
454
|
+
else
|
455
|
+
connection.begin_db_transaction
|
456
|
+
end
|
372
457
|
else
|
373
|
-
connection.
|
458
|
+
connection.begin_deferred_transaction(isolation_level)
|
374
459
|
end
|
375
460
|
|
376
461
|
super
|
@@ -391,13 +476,19 @@ module ActiveRecord
|
|
391
476
|
end
|
392
477
|
|
393
478
|
def rollback
|
394
|
-
|
479
|
+
if materialized?
|
480
|
+
connection.rollback_db_transaction
|
481
|
+
connection.reset_isolation_level if isolation_level
|
482
|
+
end
|
395
483
|
@state.full_rollback!
|
396
484
|
@instrumenter.finish(:rollback) if materialized?
|
397
485
|
end
|
398
486
|
|
399
487
|
def commit
|
400
|
-
|
488
|
+
if materialized?
|
489
|
+
connection.commit_db_transaction
|
490
|
+
connection.reset_isolation_level if isolation_level
|
491
|
+
end
|
401
492
|
@state.full_commit!
|
402
493
|
@instrumenter.finish(:commit) if materialized?
|
403
494
|
end
|
@@ -532,9 +623,7 @@ module ActiveRecord
|
|
532
623
|
@connection.lock.synchronize do
|
533
624
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
534
625
|
begin
|
535
|
-
|
536
|
-
completed = true
|
537
|
-
ret
|
626
|
+
yield transaction.user_transaction
|
538
627
|
rescue Exception => error
|
539
628
|
rollback_transaction
|
540
629
|
after_failure_actions(transaction, error)
|
@@ -542,24 +631,8 @@ module ActiveRecord
|
|
542
631
|
raise
|
543
632
|
ensure
|
544
633
|
unless error
|
545
|
-
# In 7.1 we enforce timeout >= 0.4.0 which no longer use throw, so we can
|
546
|
-
# go back to the original behavior of committing on non-local return.
|
547
|
-
# If users are using throw, we assume it's not an error case.
|
548
|
-
completed = true if ActiveRecord.commit_transaction_on_non_local_return
|
549
|
-
|
550
634
|
if Thread.current.status == "aborting"
|
551
635
|
rollback_transaction
|
552
|
-
elsif !completed && transaction.written
|
553
|
-
ActiveRecord.deprecator.warn(<<~EOW)
|
554
|
-
A transaction is being rolled back because the transaction block was
|
555
|
-
exited using `return`, `break` or `throw`.
|
556
|
-
In Rails 7.2 this transaction will be committed instead.
|
557
|
-
To opt-in to the new behavior now and suppress this warning
|
558
|
-
you can set:
|
559
|
-
|
560
|
-
Rails.application.config.active_record.commit_transaction_on_non_local_return = true
|
561
|
-
EOW
|
562
|
-
rollback_transaction
|
563
636
|
else
|
564
637
|
begin
|
565
638
|
commit_transaction
|
@@ -590,7 +663,7 @@ module ActiveRecord
|
|
590
663
|
end
|
591
664
|
|
592
665
|
private
|
593
|
-
NULL_TRANSACTION = NullTransaction.new
|
666
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
594
667
|
|
595
668
|
# Deallocate invalidated prepared statements outside of the transaction
|
596
669
|
def after_failure_actions(transaction, error)
|