activerecord 7.0.8 → 7.1.0
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 +1401 -1513
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +295 -199
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -108
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +347 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +337 -176
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +134 -146
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +29 -8
- data/lib/active_record/fixtures.rb +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +60 -40
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +184 -34
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +108 -46
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +162 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +46 -11
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
|
+
# = Active Record Connection Adapters Transaction State
|
5
6
|
class TransactionState
|
6
7
|
def initialize(state = nil)
|
7
8
|
@state = state
|
@@ -73,6 +74,35 @@ module ActiveRecord
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
77
|
+
class TransactionInstrumenter
|
78
|
+
def initialize(payload = {})
|
79
|
+
@handle = nil
|
80
|
+
@started = false
|
81
|
+
@payload = nil
|
82
|
+
@base_payload = payload
|
83
|
+
end
|
84
|
+
|
85
|
+
class InstrumentationNotStartedError < ActiveRecordError; end
|
86
|
+
class InstrumentationAlreadyStartedError < ActiveRecordError; end
|
87
|
+
|
88
|
+
def start
|
89
|
+
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
90
|
+
@started = true
|
91
|
+
|
92
|
+
@payload = @base_payload.dup
|
93
|
+
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
94
|
+
@handle.start
|
95
|
+
end
|
96
|
+
|
97
|
+
def finish(outcome)
|
98
|
+
raise InstrumentationNotStartedError.new("Called finish on a transaction that hasn't started") unless @started
|
99
|
+
@started = false
|
100
|
+
|
101
|
+
@payload[:outcome] = outcome
|
102
|
+
@handle.finish
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
76
106
|
class NullTransaction # :nodoc:
|
77
107
|
def initialize; end
|
78
108
|
def state; end
|
@@ -80,12 +110,19 @@ module ActiveRecord
|
|
80
110
|
def open?; false; end
|
81
111
|
def joinable?; false; end
|
82
112
|
def add_record(record, _ = true); end
|
113
|
+
def restartable?; false; end
|
114
|
+
def dirty?; false; end
|
115
|
+
def dirty!; end
|
116
|
+
def invalidated?; false; end
|
117
|
+
def invalidate!; end
|
83
118
|
end
|
84
119
|
|
85
120
|
class Transaction # :nodoc:
|
86
121
|
attr_reader :connection, :state, :savepoint_name, :isolation_level
|
87
122
|
attr_accessor :written
|
88
123
|
|
124
|
+
delegate :invalidate!, :invalidated?, to: :@state
|
125
|
+
|
89
126
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
90
127
|
@connection = connection
|
91
128
|
@state = TransactionState.new
|
@@ -95,6 +132,16 @@ module ActiveRecord
|
|
95
132
|
@joinable = joinable
|
96
133
|
@run_commit_callbacks = run_commit_callbacks
|
97
134
|
@lazy_enrollment_records = nil
|
135
|
+
@dirty = false
|
136
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection)
|
137
|
+
end
|
138
|
+
|
139
|
+
def dirty!
|
140
|
+
@dirty = true
|
141
|
+
end
|
142
|
+
|
143
|
+
def dirty?
|
144
|
+
@dirty
|
98
145
|
end
|
99
146
|
|
100
147
|
def add_record(record, ensure_finalize = true)
|
@@ -115,22 +162,41 @@ module ActiveRecord
|
|
115
162
|
@records
|
116
163
|
end
|
117
164
|
|
165
|
+
# Can this transaction's current state be recreated by
|
166
|
+
# rollback+begin ?
|
167
|
+
def restartable?
|
168
|
+
joinable? && !dirty?
|
169
|
+
end
|
170
|
+
|
171
|
+
def incomplete!
|
172
|
+
@instrumenter.finish(:incomplete) if materialized?
|
173
|
+
end
|
174
|
+
|
118
175
|
def materialize!
|
119
176
|
@materialized = true
|
177
|
+
@instrumenter.start
|
120
178
|
end
|
121
179
|
|
122
180
|
def materialized?
|
123
181
|
@materialized
|
124
182
|
end
|
125
183
|
|
184
|
+
def restore!
|
185
|
+
if materialized?
|
186
|
+
incomplete!
|
187
|
+
@materialized = false
|
188
|
+
materialize!
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
126
192
|
def rollback_records
|
127
193
|
return unless records
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
194
|
+
|
195
|
+
ite = unique_records
|
196
|
+
|
197
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
198
|
+
|
199
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
134
200
|
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
135
201
|
end
|
136
202
|
ensure
|
@@ -140,20 +206,38 @@ module ActiveRecord
|
|
140
206
|
end
|
141
207
|
|
142
208
|
def before_commit_records
|
143
|
-
|
209
|
+
return unless records
|
210
|
+
|
211
|
+
if @run_commit_callbacks
|
212
|
+
if ActiveRecord.before_committed_on_all_records
|
213
|
+
ite = unique_records
|
214
|
+
|
215
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
216
|
+
candidates[record] = record
|
217
|
+
end
|
218
|
+
|
219
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
220
|
+
record.before_committed! if should_run_callbacks
|
221
|
+
end
|
222
|
+
else
|
223
|
+
records.uniq.each(&:before_committed!)
|
224
|
+
end
|
225
|
+
end
|
144
226
|
end
|
145
227
|
|
146
228
|
def commit_records
|
147
229
|
return unless records
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
230
|
+
|
231
|
+
ite = unique_records
|
232
|
+
|
233
|
+
if @run_commit_callbacks
|
234
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
235
|
+
|
236
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
155
237
|
record.committed!(should_run_callbacks: should_run_callbacks)
|
156
|
-
|
238
|
+
end
|
239
|
+
else
|
240
|
+
while record = ite.shift
|
157
241
|
# if not running callbacks, only adds the record to the parent transaction
|
158
242
|
connection.add_transaction_record(record)
|
159
243
|
end
|
@@ -166,8 +250,76 @@ module ActiveRecord
|
|
166
250
|
def joinable?; @joinable; end
|
167
251
|
def closed?; false; end
|
168
252
|
def open?; !closed?; end
|
253
|
+
|
254
|
+
private
|
255
|
+
def unique_records
|
256
|
+
records.uniq(&:__id__)
|
257
|
+
end
|
258
|
+
|
259
|
+
def run_action_on_records(records, instances_to_run_callbacks_on)
|
260
|
+
while record = records.shift
|
261
|
+
should_run_callbacks = record.__id__ == instances_to_run_callbacks_on[record].__id__
|
262
|
+
|
263
|
+
yield record, should_run_callbacks
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def prepare_instances_to_run_callbacks_on(records)
|
268
|
+
records.each_with_object({}) do |record, candidates|
|
269
|
+
next unless record.trigger_transactional_callbacks?
|
270
|
+
|
271
|
+
earlier_saved_candidate = candidates[record]
|
272
|
+
|
273
|
+
next if earlier_saved_candidate && record.class.run_commit_callbacks_on_first_saved_instances_in_transaction
|
274
|
+
|
275
|
+
# If the candidate instance destroyed itself in the database, then
|
276
|
+
# instances which were added to the transaction afterwards, and which
|
277
|
+
# think they updated themselves, are wrong. They should not replace
|
278
|
+
# our candidate as an instance to run callbacks on
|
279
|
+
next if earlier_saved_candidate&.destroyed? && !record.destroyed?
|
280
|
+
|
281
|
+
# If the candidate instance was created inside of this transaction,
|
282
|
+
# then instances which were subsequently loaded from the database
|
283
|
+
# and updated need that state transferred to them so that
|
284
|
+
# the after_create_commit callbacks are run
|
285
|
+
record._new_record_before_last_commit = true if earlier_saved_candidate&._new_record_before_last_commit
|
286
|
+
|
287
|
+
# The last instance to save itself is likeliest to have internal
|
288
|
+
# state that matches what's committed to the database
|
289
|
+
candidates[record] = record
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# = Active Record Restart Parent \Transaction
|
295
|
+
class RestartParentTransaction < Transaction
|
296
|
+
def initialize(connection, parent_transaction, **options)
|
297
|
+
super(connection, **options)
|
298
|
+
|
299
|
+
@parent = parent_transaction
|
300
|
+
|
301
|
+
if isolation_level
|
302
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
|
303
|
+
end
|
304
|
+
|
305
|
+
@parent.state.add_child(@state)
|
306
|
+
end
|
307
|
+
|
308
|
+
delegate :materialize!, :materialized?, :restart, to: :@parent
|
309
|
+
|
310
|
+
def rollback
|
311
|
+
@state.rollback!
|
312
|
+
@parent.restart
|
313
|
+
end
|
314
|
+
|
315
|
+
def commit
|
316
|
+
@state.commit!
|
317
|
+
end
|
318
|
+
|
319
|
+
def full_rollback?; false; end
|
169
320
|
end
|
170
321
|
|
322
|
+
# = Active Record Savepoint \Transaction
|
171
323
|
class SavepointTransaction < Transaction
|
172
324
|
def initialize(connection, savepoint_name, parent_transaction, **options)
|
173
325
|
super(connection, **options)
|
@@ -186,19 +338,33 @@ module ActiveRecord
|
|
186
338
|
super
|
187
339
|
end
|
188
340
|
|
341
|
+
def restart
|
342
|
+
return unless materialized?
|
343
|
+
|
344
|
+
@instrumenter.finish(:restart)
|
345
|
+
@instrumenter.start
|
346
|
+
|
347
|
+
connection.rollback_to_savepoint(savepoint_name)
|
348
|
+
end
|
349
|
+
|
189
350
|
def rollback
|
190
|
-
|
351
|
+
unless @state.invalidated?
|
352
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
353
|
+
end
|
191
354
|
@state.rollback!
|
355
|
+
@instrumenter.finish(:rollback) if materialized?
|
192
356
|
end
|
193
357
|
|
194
358
|
def commit
|
195
359
|
connection.release_savepoint(savepoint_name) if materialized?
|
196
360
|
@state.commit!
|
361
|
+
@instrumenter.finish(:commit) if materialized?
|
197
362
|
end
|
198
363
|
|
199
364
|
def full_rollback?; false; end
|
200
365
|
end
|
201
366
|
|
367
|
+
# = Active Record Real \Transaction
|
202
368
|
class RealTransaction < Transaction
|
203
369
|
def materialize!
|
204
370
|
if isolation_level
|
@@ -210,14 +376,30 @@ module ActiveRecord
|
|
210
376
|
super
|
211
377
|
end
|
212
378
|
|
379
|
+
def restart
|
380
|
+
return unless materialized?
|
381
|
+
|
382
|
+
@instrumenter.finish(:restart)
|
383
|
+
|
384
|
+
if connection.supports_restart_db_transaction?
|
385
|
+
@instrumenter.start
|
386
|
+
connection.restart_db_transaction
|
387
|
+
else
|
388
|
+
connection.rollback_db_transaction
|
389
|
+
materialize!
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
213
393
|
def rollback
|
214
394
|
connection.rollback_db_transaction if materialized?
|
215
395
|
@state.full_rollback!
|
396
|
+
@instrumenter.finish(:rollback) if materialized?
|
216
397
|
end
|
217
398
|
|
218
399
|
def commit
|
219
400
|
connection.commit_db_transaction if materialized?
|
220
401
|
@state.full_commit!
|
402
|
+
@instrumenter.finish(:commit) if materialized?
|
221
403
|
end
|
222
404
|
end
|
223
405
|
|
@@ -241,21 +423,31 @@ module ActiveRecord
|
|
241
423
|
joinable: joinable,
|
242
424
|
run_commit_callbacks: run_commit_callbacks
|
243
425
|
)
|
426
|
+
elsif current_transaction.restartable?
|
427
|
+
RestartParentTransaction.new(
|
428
|
+
@connection,
|
429
|
+
current_transaction,
|
430
|
+
isolation: isolation,
|
431
|
+
joinable: joinable,
|
432
|
+
run_commit_callbacks: run_commit_callbacks
|
433
|
+
)
|
244
434
|
else
|
245
435
|
SavepointTransaction.new(
|
246
436
|
@connection,
|
247
437
|
"active_record_#{@stack.size}",
|
248
|
-
|
438
|
+
current_transaction,
|
249
439
|
isolation: isolation,
|
250
440
|
joinable: joinable,
|
251
441
|
run_commit_callbacks: run_commit_callbacks
|
252
442
|
)
|
253
443
|
end
|
254
444
|
|
255
|
-
|
256
|
-
@
|
257
|
-
|
258
|
-
|
445
|
+
unless transaction.materialized?
|
446
|
+
if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
|
447
|
+
@has_unmaterialized_transactions = true
|
448
|
+
else
|
449
|
+
transaction.materialize!
|
450
|
+
end
|
259
451
|
end
|
260
452
|
@stack.push(transaction)
|
261
453
|
transaction
|
@@ -275,18 +467,35 @@ module ActiveRecord
|
|
275
467
|
@lazy_transactions_enabled
|
276
468
|
end
|
277
469
|
|
470
|
+
def dirty_current_transaction
|
471
|
+
current_transaction.dirty!
|
472
|
+
end
|
473
|
+
|
474
|
+
def restore_transactions
|
475
|
+
return false unless restorable?
|
476
|
+
|
477
|
+
@stack.each(&:restore!)
|
478
|
+
|
479
|
+
true
|
480
|
+
end
|
481
|
+
|
482
|
+
def restorable?
|
483
|
+
@stack.none?(&:dirty?)
|
484
|
+
end
|
485
|
+
|
278
486
|
def materialize_transactions
|
279
487
|
return if @materializing_transactions
|
280
|
-
return unless @has_unmaterialized_transactions
|
281
488
|
|
282
|
-
@
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
489
|
+
if @has_unmaterialized_transactions
|
490
|
+
@connection.lock.synchronize do
|
491
|
+
begin
|
492
|
+
@materializing_transactions = true
|
493
|
+
@stack.each { |t| t.materialize! unless t.materialized? }
|
494
|
+
ensure
|
495
|
+
@materializing_transactions = false
|
496
|
+
end
|
497
|
+
@has_unmaterialized_transactions = false
|
288
498
|
end
|
289
|
-
@has_unmaterialized_transactions = false
|
290
499
|
end
|
291
500
|
end
|
292
501
|
|
@@ -300,6 +509,8 @@ module ActiveRecord
|
|
300
509
|
@stack.pop
|
301
510
|
end
|
302
511
|
|
512
|
+
dirty_current_transaction if transaction.dirty?
|
513
|
+
|
303
514
|
transaction.commit
|
304
515
|
transaction.commit_records
|
305
516
|
end
|
@@ -307,8 +518,12 @@ module ActiveRecord
|
|
307
518
|
|
308
519
|
def rollback_transaction(transaction = nil)
|
309
520
|
@connection.lock.synchronize do
|
310
|
-
transaction ||= @stack.
|
311
|
-
|
521
|
+
transaction ||= @stack.last
|
522
|
+
begin
|
523
|
+
transaction.rollback
|
524
|
+
ensure
|
525
|
+
@stack.pop if @stack.last == transaction
|
526
|
+
end
|
312
527
|
transaction.rollback_records
|
313
528
|
end
|
314
529
|
end
|
@@ -316,39 +531,53 @@ module ActiveRecord
|
|
316
531
|
def within_new_transaction(isolation: nil, joinable: true)
|
317
532
|
@connection.lock.synchronize do
|
318
533
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
transaction.state.invalidate! if error.is_a? ActiveRecord::TransactionRollbackError
|
534
|
+
begin
|
535
|
+
ret = yield
|
536
|
+
completed = true
|
537
|
+
ret
|
538
|
+
rescue Exception => error
|
325
539
|
rollback_transaction
|
326
540
|
after_failure_actions(transaction, error)
|
327
|
-
end
|
328
541
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
#
|
334
|
-
#
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
542
|
+
raise
|
543
|
+
ensure
|
544
|
+
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
|
+
if Thread.current.status == "aborting"
|
551
|
+
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
|
+
else
|
564
|
+
begin
|
565
|
+
commit_transaction
|
566
|
+
rescue ActiveRecord::ConnectionFailed
|
567
|
+
transaction.invalidate! unless transaction.state.completed?
|
568
|
+
raise
|
569
|
+
rescue Exception
|
570
|
+
rollback_transaction(transaction) unless transaction.state.completed?
|
571
|
+
raise
|
572
|
+
end
|
349
573
|
end
|
350
574
|
end
|
351
575
|
end
|
576
|
+
ensure
|
577
|
+
unless transaction&.state&.completed?
|
578
|
+
@connection.throw_away!
|
579
|
+
transaction&.incomplete!
|
580
|
+
end
|
352
581
|
end
|
353
582
|
end
|
354
583
|
|