activerecord 6.1.7 → 7.2.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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +170 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +103 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
7
|
+
# = Active Record Connection Adapters Transaction State
|
5
8
|
class TransactionState
|
6
9
|
def initialize(state = nil)
|
7
10
|
@state = state
|
@@ -73,28 +76,110 @@ module ActiveRecord
|
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
76
|
-
class
|
77
|
-
def initialize
|
79
|
+
class TransactionInstrumenter
|
80
|
+
def initialize(payload = {})
|
81
|
+
@handle = nil
|
82
|
+
@started = false
|
83
|
+
@payload = nil
|
84
|
+
@base_payload = payload
|
85
|
+
end
|
86
|
+
|
87
|
+
class InstrumentationNotStartedError < ActiveRecordError; end
|
88
|
+
class InstrumentationAlreadyStartedError < ActiveRecordError; end
|
89
|
+
|
90
|
+
def start
|
91
|
+
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
92
|
+
@started = true
|
93
|
+
|
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.
|
97
|
+
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
98
|
+
@handle.start
|
99
|
+
end
|
100
|
+
|
101
|
+
def finish(outcome)
|
102
|
+
raise InstrumentationNotStartedError.new("Called finish on a transaction that hasn't started") unless @started
|
103
|
+
@started = false
|
104
|
+
|
105
|
+
@payload[:outcome] = outcome
|
106
|
+
@handle.finish
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class NullTransaction # :nodoc:
|
78
111
|
def state; end
|
79
112
|
def closed?; true; end
|
80
113
|
def open?; false; end
|
81
114
|
def joinable?; false; end
|
82
115
|
def add_record(record, _ = true); end
|
116
|
+
def restartable?; false; end
|
117
|
+
def dirty?; false; end
|
118
|
+
def dirty!; end
|
119
|
+
def invalidated?; false; end
|
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
|
83
126
|
end
|
84
127
|
|
85
|
-
class Transaction
|
86
|
-
|
128
|
+
class Transaction # :nodoc:
|
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
|
87
149
|
attr_accessor :written
|
88
150
|
|
151
|
+
delegate :invalidate!, :invalidated?, to: :@state
|
152
|
+
|
89
153
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
154
|
+
super()
|
90
155
|
@connection = connection
|
91
156
|
@state = TransactionState.new
|
157
|
+
@callbacks = nil
|
92
158
|
@records = nil
|
93
159
|
@isolation_level = isolation
|
94
160
|
@materialized = false
|
95
161
|
@joinable = joinable
|
96
162
|
@run_commit_callbacks = run_commit_callbacks
|
97
163
|
@lazy_enrollment_records = nil
|
164
|
+
@dirty = false
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
167
|
+
end
|
168
|
+
|
169
|
+
def dirty!
|
170
|
+
@dirty = true
|
171
|
+
end
|
172
|
+
|
173
|
+
def dirty?
|
174
|
+
@dirty
|
175
|
+
end
|
176
|
+
|
177
|
+
def open?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed?
|
182
|
+
false
|
98
183
|
end
|
99
184
|
|
100
185
|
def add_record(record, ensure_finalize = true)
|
@@ -107,6 +192,30 @@ module ActiveRecord
|
|
107
192
|
end
|
108
193
|
end
|
109
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
|
+
|
110
219
|
def records
|
111
220
|
if @lazy_enrollment_records
|
112
221
|
@records.concat @lazy_enrollment_records.values
|
@@ -115,59 +224,183 @@ module ActiveRecord
|
|
115
224
|
@records
|
116
225
|
end
|
117
226
|
|
227
|
+
# Can this transaction's current state be recreated by
|
228
|
+
# rollback+begin ?
|
229
|
+
def restartable?
|
230
|
+
joinable? && !dirty?
|
231
|
+
end
|
232
|
+
|
233
|
+
def incomplete!
|
234
|
+
@instrumenter.finish(:incomplete) if materialized?
|
235
|
+
end
|
236
|
+
|
118
237
|
def materialize!
|
119
238
|
@materialized = true
|
239
|
+
@instrumenter.start
|
120
240
|
end
|
121
241
|
|
122
242
|
def materialized?
|
123
243
|
@materialized
|
124
244
|
end
|
125
245
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
trigger_callbacks = record.trigger_transactional_callbacks?
|
132
|
-
should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
|
133
|
-
already_run_callbacks[record] ||= trigger_callbacks
|
134
|
-
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
246
|
+
def restore!
|
247
|
+
if materialized?
|
248
|
+
incomplete!
|
249
|
+
@materialized = false
|
250
|
+
materialize!
|
135
251
|
end
|
136
|
-
|
137
|
-
|
138
|
-
|
252
|
+
end
|
253
|
+
|
254
|
+
def rollback_records
|
255
|
+
if records
|
256
|
+
begin
|
257
|
+
ite = unique_records
|
258
|
+
|
259
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
260
|
+
|
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
|
139
269
|
end
|
270
|
+
|
271
|
+
@callbacks&.each(&:after_rollback)
|
140
272
|
end
|
141
273
|
|
142
274
|
def before_commit_records
|
143
|
-
|
275
|
+
if @run_commit_callbacks
|
276
|
+
if records
|
277
|
+
if ActiveRecord.before_committed_on_all_records
|
278
|
+
ite = unique_records
|
279
|
+
|
280
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
281
|
+
candidates[record] = record
|
282
|
+
end
|
283
|
+
|
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!)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
@callbacks&.each(&:before_commit)
|
293
|
+
end
|
294
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
295
|
+
# remaining callbacks to the parent transaction
|
144
296
|
end
|
145
297
|
|
146
298
|
def commit_records
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
299
|
+
if records
|
300
|
+
begin
|
301
|
+
ite = unique_records
|
302
|
+
|
303
|
+
if @run_commit_callbacks
|
304
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
305
|
+
|
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) }
|
159
317
|
end
|
160
318
|
end
|
161
|
-
|
162
|
-
|
319
|
+
|
320
|
+
if @run_commit_callbacks
|
321
|
+
@callbacks&.each(&:after_commit)
|
322
|
+
elsif @callbacks
|
323
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
324
|
+
end
|
163
325
|
end
|
164
326
|
|
165
327
|
def full_rollback?; true; end
|
166
328
|
def joinable?; @joinable; end
|
167
|
-
|
168
|
-
|
329
|
+
|
330
|
+
protected
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
def unique_records
|
337
|
+
records.uniq(&:__id__)
|
338
|
+
end
|
339
|
+
|
340
|
+
def run_action_on_records(records, instances_to_run_callbacks_on)
|
341
|
+
while record = records.shift
|
342
|
+
should_run_callbacks = record.__id__ == instances_to_run_callbacks_on[record].__id__
|
343
|
+
|
344
|
+
yield record, should_run_callbacks
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def prepare_instances_to_run_callbacks_on(records)
|
349
|
+
records.each_with_object({}) do |record, candidates|
|
350
|
+
next unless record.trigger_transactional_callbacks?
|
351
|
+
|
352
|
+
earlier_saved_candidate = candidates[record]
|
353
|
+
|
354
|
+
next if earlier_saved_candidate && record.class.run_commit_callbacks_on_first_saved_instances_in_transaction
|
355
|
+
|
356
|
+
# If the candidate instance destroyed itself in the database, then
|
357
|
+
# instances which were added to the transaction afterwards, and which
|
358
|
+
# think they updated themselves, are wrong. They should not replace
|
359
|
+
# our candidate as an instance to run callbacks on
|
360
|
+
next if earlier_saved_candidate&.destroyed? && !record.destroyed?
|
361
|
+
|
362
|
+
# If the candidate instance was created inside of this transaction,
|
363
|
+
# then instances which were subsequently loaded from the database
|
364
|
+
# and updated need that state transferred to them so that
|
365
|
+
# the after_create_commit callbacks are run
|
366
|
+
record._new_record_before_last_commit = true if earlier_saved_candidate&._new_record_before_last_commit
|
367
|
+
|
368
|
+
# The last instance to save itself is likeliest to have internal
|
369
|
+
# state that matches what's committed to the database
|
370
|
+
candidates[record] = record
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# = Active Record Restart Parent \Transaction
|
376
|
+
class RestartParentTransaction < Transaction
|
377
|
+
def initialize(connection, parent_transaction, **options)
|
378
|
+
super(connection, **options)
|
379
|
+
|
380
|
+
@parent = parent_transaction
|
381
|
+
|
382
|
+
if isolation_level
|
383
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
|
384
|
+
end
|
385
|
+
|
386
|
+
@parent.state.add_child(@state)
|
387
|
+
end
|
388
|
+
|
389
|
+
delegate :materialize!, :materialized?, :restart, to: :@parent
|
390
|
+
|
391
|
+
def rollback
|
392
|
+
@state.rollback!
|
393
|
+
@parent.restart
|
394
|
+
end
|
395
|
+
|
396
|
+
def commit
|
397
|
+
@state.commit!
|
398
|
+
end
|
399
|
+
|
400
|
+
def full_rollback?; false; end
|
169
401
|
end
|
170
402
|
|
403
|
+
# = Active Record Savepoint \Transaction
|
171
404
|
class SavepointTransaction < Transaction
|
172
405
|
def initialize(connection, savepoint_name, parent_transaction, **options)
|
173
406
|
super(connection, **options)
|
@@ -186,19 +419,33 @@ module ActiveRecord
|
|
186
419
|
super
|
187
420
|
end
|
188
421
|
|
422
|
+
def restart
|
423
|
+
return unless materialized?
|
424
|
+
|
425
|
+
@instrumenter.finish(:restart)
|
426
|
+
@instrumenter.start
|
427
|
+
|
428
|
+
connection.rollback_to_savepoint(savepoint_name)
|
429
|
+
end
|
430
|
+
|
189
431
|
def rollback
|
190
|
-
|
432
|
+
unless @state.invalidated?
|
433
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
434
|
+
end
|
191
435
|
@state.rollback!
|
436
|
+
@instrumenter.finish(:rollback) if materialized?
|
192
437
|
end
|
193
438
|
|
194
439
|
def commit
|
195
440
|
connection.release_savepoint(savepoint_name) if materialized?
|
196
441
|
@state.commit!
|
442
|
+
@instrumenter.finish(:commit) if materialized?
|
197
443
|
end
|
198
444
|
|
199
445
|
def full_rollback?; false; end
|
200
446
|
end
|
201
447
|
|
448
|
+
# = Active Record Real \Transaction
|
202
449
|
class RealTransaction < Transaction
|
203
450
|
def materialize!
|
204
451
|
if isolation_level
|
@@ -210,18 +457,34 @@ module ActiveRecord
|
|
210
457
|
super
|
211
458
|
end
|
212
459
|
|
460
|
+
def restart
|
461
|
+
return unless materialized?
|
462
|
+
|
463
|
+
@instrumenter.finish(:restart)
|
464
|
+
|
465
|
+
if connection.supports_restart_db_transaction?
|
466
|
+
@instrumenter.start
|
467
|
+
connection.restart_db_transaction
|
468
|
+
else
|
469
|
+
connection.rollback_db_transaction
|
470
|
+
materialize!
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
213
474
|
def rollback
|
214
475
|
connection.rollback_db_transaction if materialized?
|
215
476
|
@state.full_rollback!
|
477
|
+
@instrumenter.finish(:rollback) if materialized?
|
216
478
|
end
|
217
479
|
|
218
480
|
def commit
|
219
481
|
connection.commit_db_transaction if materialized?
|
220
482
|
@state.full_commit!
|
483
|
+
@instrumenter.finish(:commit) if materialized?
|
221
484
|
end
|
222
485
|
end
|
223
486
|
|
224
|
-
class TransactionManager
|
487
|
+
class TransactionManager # :nodoc:
|
225
488
|
def initialize(connection)
|
226
489
|
@stack = []
|
227
490
|
@connection = connection
|
@@ -241,21 +504,31 @@ module ActiveRecord
|
|
241
504
|
joinable: joinable,
|
242
505
|
run_commit_callbacks: run_commit_callbacks
|
243
506
|
)
|
507
|
+
elsif current_transaction.restartable?
|
508
|
+
RestartParentTransaction.new(
|
509
|
+
@connection,
|
510
|
+
current_transaction,
|
511
|
+
isolation: isolation,
|
512
|
+
joinable: joinable,
|
513
|
+
run_commit_callbacks: run_commit_callbacks
|
514
|
+
)
|
244
515
|
else
|
245
516
|
SavepointTransaction.new(
|
246
517
|
@connection,
|
247
518
|
"active_record_#{@stack.size}",
|
248
|
-
|
519
|
+
current_transaction,
|
249
520
|
isolation: isolation,
|
250
521
|
joinable: joinable,
|
251
522
|
run_commit_callbacks: run_commit_callbacks
|
252
523
|
)
|
253
524
|
end
|
254
525
|
|
255
|
-
|
256
|
-
@
|
257
|
-
|
258
|
-
|
526
|
+
unless transaction.materialized?
|
527
|
+
if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
|
528
|
+
@has_unmaterialized_transactions = true
|
529
|
+
else
|
530
|
+
transaction.materialize!
|
531
|
+
end
|
259
532
|
end
|
260
533
|
@stack.push(transaction)
|
261
534
|
transaction
|
@@ -275,18 +548,35 @@ module ActiveRecord
|
|
275
548
|
@lazy_transactions_enabled
|
276
549
|
end
|
277
550
|
|
551
|
+
def dirty_current_transaction
|
552
|
+
current_transaction.dirty!
|
553
|
+
end
|
554
|
+
|
555
|
+
def restore_transactions
|
556
|
+
return false unless restorable?
|
557
|
+
|
558
|
+
@stack.each(&:restore!)
|
559
|
+
|
560
|
+
true
|
561
|
+
end
|
562
|
+
|
563
|
+
def restorable?
|
564
|
+
@stack.none?(&:dirty?)
|
565
|
+
end
|
566
|
+
|
278
567
|
def materialize_transactions
|
279
568
|
return if @materializing_transactions
|
280
|
-
return unless @has_unmaterialized_transactions
|
281
569
|
|
282
|
-
@
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
570
|
+
if @has_unmaterialized_transactions
|
571
|
+
@connection.lock.synchronize do
|
572
|
+
begin
|
573
|
+
@materializing_transactions = true
|
574
|
+
@stack.each { |t| t.materialize! unless t.materialized? }
|
575
|
+
ensure
|
576
|
+
@materializing_transactions = false
|
577
|
+
end
|
578
|
+
@has_unmaterialized_transactions = false
|
288
579
|
end
|
289
|
-
@has_unmaterialized_transactions = false
|
290
580
|
end
|
291
581
|
end
|
292
582
|
|
@@ -300,6 +590,8 @@ module ActiveRecord
|
|
300
590
|
@stack.pop
|
301
591
|
end
|
302
592
|
|
593
|
+
dirty_current_transaction if transaction.dirty?
|
594
|
+
|
303
595
|
transaction.commit
|
304
596
|
transaction.commit_records
|
305
597
|
end
|
@@ -307,8 +599,12 @@ module ActiveRecord
|
|
307
599
|
|
308
600
|
def rollback_transaction(transaction = nil)
|
309
601
|
@connection.lock.synchronize do
|
310
|
-
transaction ||= @stack.
|
311
|
-
|
602
|
+
transaction ||= @stack.last
|
603
|
+
begin
|
604
|
+
transaction.rollback
|
605
|
+
ensure
|
606
|
+
@stack.pop if @stack.last == transaction
|
607
|
+
end
|
312
608
|
transaction.rollback_records
|
313
609
|
end
|
314
610
|
end
|
@@ -316,39 +612,23 @@ module ActiveRecord
|
|
316
612
|
def within_new_transaction(isolation: nil, joinable: true)
|
317
613
|
@connection.lock.synchronize do
|
318
614
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
rescue Exception => error
|
323
|
-
if transaction
|
324
|
-
transaction.state.invalidate! if error.is_a? ActiveRecord::TransactionRollbackError
|
615
|
+
begin
|
616
|
+
yield transaction.user_transaction
|
617
|
+
rescue Exception => error
|
325
618
|
rollback_transaction
|
326
619
|
after_failure_actions(transaction, error)
|
327
|
-
end
|
328
620
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
if error
|
333
|
-
# @connection still holds an open or invalid transaction, so we must not
|
334
|
-
# put it back in the pool for reuse.
|
335
|
-
@connection.throw_away! unless transaction.state.rolledback?
|
336
|
-
else
|
621
|
+
raise
|
622
|
+
ensure
|
623
|
+
unless error
|
337
624
|
if Thread.current.status == "aborting"
|
338
625
|
rollback_transaction
|
339
626
|
else
|
340
|
-
if !completed && transaction.written
|
341
|
-
ActiveSupport::Deprecation.warn(<<~EOW)
|
342
|
-
Using `return`, `break` or `throw` to exit a transaction block is
|
343
|
-
deprecated without replacement. If the `throw` came from
|
344
|
-
`Timeout.timeout(duration)`, pass an exception class as a second
|
345
|
-
argument so it doesn't use `throw` to abort its block. This results
|
346
|
-
in the transaction being committed, but in the next release of Rails
|
347
|
-
it will rollback.
|
348
|
-
EOW
|
349
|
-
end
|
350
627
|
begin
|
351
628
|
commit_transaction
|
629
|
+
rescue ActiveRecord::ConnectionFailed
|
630
|
+
transaction.invalidate! unless transaction.state.completed?
|
631
|
+
raise
|
352
632
|
rescue Exception
|
353
633
|
rollback_transaction(transaction) unless transaction.state.completed?
|
354
634
|
raise
|
@@ -356,6 +636,11 @@ module ActiveRecord
|
|
356
636
|
end
|
357
637
|
end
|
358
638
|
end
|
639
|
+
ensure
|
640
|
+
unless transaction&.state&.completed?
|
641
|
+
@connection.throw_away!
|
642
|
+
transaction&.incomplete!
|
643
|
+
end
|
359
644
|
end
|
360
645
|
end
|
361
646
|
|
@@ -368,7 +653,7 @@ module ActiveRecord
|
|
368
653
|
end
|
369
654
|
|
370
655
|
private
|
371
|
-
NULL_TRANSACTION = NullTransaction.new
|
656
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
372
657
|
|
373
658
|
# Deallocate invalidated prepared statements outside of the transaction
|
374
659
|
def after_failure_actions(transaction, error)
|