activerecord 7.0.8 → 7.1.0.rc1
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 +1388 -1520
- 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 +12 -9
- 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 +283 -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 +9 -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 +336 -173
- 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 +4 -4
- 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 +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +183 -33
- 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 +14 -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 +48 -13
- 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,32 @@ 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
|
+
def start
|
86
|
+
return if @started
|
87
|
+
@started = true
|
88
|
+
|
89
|
+
@payload = @base_payload.dup
|
90
|
+
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
91
|
+
@handle.start
|
92
|
+
end
|
93
|
+
|
94
|
+
def finish(outcome)
|
95
|
+
return unless @started
|
96
|
+
@started = false
|
97
|
+
|
98
|
+
@payload[:outcome] = outcome
|
99
|
+
@handle.finish
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
76
103
|
class NullTransaction # :nodoc:
|
77
104
|
def initialize; end
|
78
105
|
def state; end
|
@@ -80,12 +107,19 @@ module ActiveRecord
|
|
80
107
|
def open?; false; end
|
81
108
|
def joinable?; false; end
|
82
109
|
def add_record(record, _ = true); end
|
110
|
+
def restartable?; false; end
|
111
|
+
def dirty?; false; end
|
112
|
+
def dirty!; end
|
113
|
+
def invalidated?; false; end
|
114
|
+
def invalidate!; end
|
83
115
|
end
|
84
116
|
|
85
117
|
class Transaction # :nodoc:
|
86
118
|
attr_reader :connection, :state, :savepoint_name, :isolation_level
|
87
119
|
attr_accessor :written
|
88
120
|
|
121
|
+
delegate :invalidate!, :invalidated?, to: :@state
|
122
|
+
|
89
123
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
90
124
|
@connection = connection
|
91
125
|
@state = TransactionState.new
|
@@ -95,6 +129,16 @@ module ActiveRecord
|
|
95
129
|
@joinable = joinable
|
96
130
|
@run_commit_callbacks = run_commit_callbacks
|
97
131
|
@lazy_enrollment_records = nil
|
132
|
+
@dirty = false
|
133
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection)
|
134
|
+
end
|
135
|
+
|
136
|
+
def dirty!
|
137
|
+
@dirty = true
|
138
|
+
end
|
139
|
+
|
140
|
+
def dirty?
|
141
|
+
@dirty
|
98
142
|
end
|
99
143
|
|
100
144
|
def add_record(record, ensure_finalize = true)
|
@@ -115,22 +159,40 @@ module ActiveRecord
|
|
115
159
|
@records
|
116
160
|
end
|
117
161
|
|
162
|
+
# Can this transaction's current state be recreated by
|
163
|
+
# rollback+begin ?
|
164
|
+
def restartable?
|
165
|
+
joinable? && !dirty?
|
166
|
+
end
|
167
|
+
|
168
|
+
def incomplete!
|
169
|
+
@instrumenter.finish(:incomplete)
|
170
|
+
end
|
171
|
+
|
118
172
|
def materialize!
|
119
173
|
@materialized = true
|
174
|
+
@instrumenter.start
|
120
175
|
end
|
121
176
|
|
122
177
|
def materialized?
|
123
178
|
@materialized
|
124
179
|
end
|
125
180
|
|
181
|
+
def restore!
|
182
|
+
if materialized?
|
183
|
+
@materialized = false
|
184
|
+
materialize!
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
126
188
|
def rollback_records
|
127
189
|
return unless records
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
190
|
+
|
191
|
+
ite = unique_records
|
192
|
+
|
193
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
194
|
+
|
195
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
134
196
|
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
135
197
|
end
|
136
198
|
ensure
|
@@ -140,20 +202,38 @@ module ActiveRecord
|
|
140
202
|
end
|
141
203
|
|
142
204
|
def before_commit_records
|
143
|
-
|
205
|
+
return unless records
|
206
|
+
|
207
|
+
if @run_commit_callbacks
|
208
|
+
if ActiveRecord.before_committed_on_all_records
|
209
|
+
ite = unique_records
|
210
|
+
|
211
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
212
|
+
candidates[record] = record
|
213
|
+
end
|
214
|
+
|
215
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
216
|
+
record.before_committed! if should_run_callbacks
|
217
|
+
end
|
218
|
+
else
|
219
|
+
records.uniq.each(&:before_committed!)
|
220
|
+
end
|
221
|
+
end
|
144
222
|
end
|
145
223
|
|
146
224
|
def commit_records
|
147
225
|
return unless records
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
226
|
+
|
227
|
+
ite = unique_records
|
228
|
+
|
229
|
+
if @run_commit_callbacks
|
230
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
231
|
+
|
232
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
155
233
|
record.committed!(should_run_callbacks: should_run_callbacks)
|
156
|
-
|
234
|
+
end
|
235
|
+
else
|
236
|
+
while record = ite.shift
|
157
237
|
# if not running callbacks, only adds the record to the parent transaction
|
158
238
|
connection.add_transaction_record(record)
|
159
239
|
end
|
@@ -166,8 +246,76 @@ module ActiveRecord
|
|
166
246
|
def joinable?; @joinable; end
|
167
247
|
def closed?; false; end
|
168
248
|
def open?; !closed?; end
|
249
|
+
|
250
|
+
private
|
251
|
+
def unique_records
|
252
|
+
records.uniq(&:__id__)
|
253
|
+
end
|
254
|
+
|
255
|
+
def run_action_on_records(records, instances_to_run_callbacks_on)
|
256
|
+
while record = records.shift
|
257
|
+
should_run_callbacks = record.__id__ == instances_to_run_callbacks_on[record].__id__
|
258
|
+
|
259
|
+
yield record, should_run_callbacks
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def prepare_instances_to_run_callbacks_on(records)
|
264
|
+
records.each_with_object({}) do |record, candidates|
|
265
|
+
next unless record.trigger_transactional_callbacks?
|
266
|
+
|
267
|
+
earlier_saved_candidate = candidates[record]
|
268
|
+
|
269
|
+
next if earlier_saved_candidate && record.class.run_commit_callbacks_on_first_saved_instances_in_transaction
|
270
|
+
|
271
|
+
# If the candidate instance destroyed itself in the database, then
|
272
|
+
# instances which were added to the transaction afterwards, and which
|
273
|
+
# think they updated themselves, are wrong. They should not replace
|
274
|
+
# our candidate as an instance to run callbacks on
|
275
|
+
next if earlier_saved_candidate&.destroyed? && !record.destroyed?
|
276
|
+
|
277
|
+
# If the candidate instance was created inside of this transaction,
|
278
|
+
# then instances which were subsequently loaded from the database
|
279
|
+
# and updated need that state transferred to them so that
|
280
|
+
# the after_create_commit callbacks are run
|
281
|
+
record._new_record_before_last_commit = true if earlier_saved_candidate&._new_record_before_last_commit
|
282
|
+
|
283
|
+
# The last instance to save itself is likeliest to have internal
|
284
|
+
# state that matches what's committed to the database
|
285
|
+
candidates[record] = record
|
286
|
+
end
|
287
|
+
end
|
169
288
|
end
|
170
289
|
|
290
|
+
# = Active Record Restart Parent \Transaction
|
291
|
+
class RestartParentTransaction < Transaction
|
292
|
+
def initialize(connection, parent_transaction, **options)
|
293
|
+
super(connection, **options)
|
294
|
+
|
295
|
+
@parent = parent_transaction
|
296
|
+
|
297
|
+
if isolation_level
|
298
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
|
299
|
+
end
|
300
|
+
|
301
|
+
@parent.state.add_child(@state)
|
302
|
+
end
|
303
|
+
|
304
|
+
delegate :materialize!, :materialized?, :restart, to: :@parent
|
305
|
+
|
306
|
+
def rollback
|
307
|
+
@state.rollback!
|
308
|
+
@parent.restart
|
309
|
+
end
|
310
|
+
|
311
|
+
def commit
|
312
|
+
@state.commit!
|
313
|
+
end
|
314
|
+
|
315
|
+
def full_rollback?; false; end
|
316
|
+
end
|
317
|
+
|
318
|
+
# = Active Record Savepoint \Transaction
|
171
319
|
class SavepointTransaction < Transaction
|
172
320
|
def initialize(connection, savepoint_name, parent_transaction, **options)
|
173
321
|
super(connection, **options)
|
@@ -186,19 +334,33 @@ module ActiveRecord
|
|
186
334
|
super
|
187
335
|
end
|
188
336
|
|
337
|
+
def restart
|
338
|
+
return unless materialized?
|
339
|
+
|
340
|
+
@instrumenter.finish(:restart)
|
341
|
+
@instrumenter.start
|
342
|
+
|
343
|
+
connection.rollback_to_savepoint(savepoint_name)
|
344
|
+
end
|
345
|
+
|
189
346
|
def rollback
|
190
|
-
|
347
|
+
unless @state.invalidated?
|
348
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
349
|
+
end
|
191
350
|
@state.rollback!
|
351
|
+
@instrumenter.finish(:rollback)
|
192
352
|
end
|
193
353
|
|
194
354
|
def commit
|
195
355
|
connection.release_savepoint(savepoint_name) if materialized?
|
196
356
|
@state.commit!
|
357
|
+
@instrumenter.finish(:commit)
|
197
358
|
end
|
198
359
|
|
199
360
|
def full_rollback?; false; end
|
200
361
|
end
|
201
362
|
|
363
|
+
# = Active Record Real \Transaction
|
202
364
|
class RealTransaction < Transaction
|
203
365
|
def materialize!
|
204
366
|
if isolation_level
|
@@ -210,14 +372,30 @@ module ActiveRecord
|
|
210
372
|
super
|
211
373
|
end
|
212
374
|
|
375
|
+
def restart
|
376
|
+
return unless materialized?
|
377
|
+
|
378
|
+
@instrumenter.finish(:restart)
|
379
|
+
|
380
|
+
if connection.supports_restart_db_transaction?
|
381
|
+
@instrumenter.start
|
382
|
+
connection.restart_db_transaction
|
383
|
+
else
|
384
|
+
connection.rollback_db_transaction
|
385
|
+
materialize!
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
213
389
|
def rollback
|
214
390
|
connection.rollback_db_transaction if materialized?
|
215
391
|
@state.full_rollback!
|
392
|
+
@instrumenter.finish(:rollback)
|
216
393
|
end
|
217
394
|
|
218
395
|
def commit
|
219
396
|
connection.commit_db_transaction if materialized?
|
220
397
|
@state.full_commit!
|
398
|
+
@instrumenter.finish(:commit)
|
221
399
|
end
|
222
400
|
end
|
223
401
|
|
@@ -241,21 +419,31 @@ module ActiveRecord
|
|
241
419
|
joinable: joinable,
|
242
420
|
run_commit_callbacks: run_commit_callbacks
|
243
421
|
)
|
422
|
+
elsif current_transaction.restartable?
|
423
|
+
RestartParentTransaction.new(
|
424
|
+
@connection,
|
425
|
+
current_transaction,
|
426
|
+
isolation: isolation,
|
427
|
+
joinable: joinable,
|
428
|
+
run_commit_callbacks: run_commit_callbacks
|
429
|
+
)
|
244
430
|
else
|
245
431
|
SavepointTransaction.new(
|
246
432
|
@connection,
|
247
433
|
"active_record_#{@stack.size}",
|
248
|
-
|
434
|
+
current_transaction,
|
249
435
|
isolation: isolation,
|
250
436
|
joinable: joinable,
|
251
437
|
run_commit_callbacks: run_commit_callbacks
|
252
438
|
)
|
253
439
|
end
|
254
440
|
|
255
|
-
|
256
|
-
@
|
257
|
-
|
258
|
-
|
441
|
+
unless transaction.materialized?
|
442
|
+
if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
|
443
|
+
@has_unmaterialized_transactions = true
|
444
|
+
else
|
445
|
+
transaction.materialize!
|
446
|
+
end
|
259
447
|
end
|
260
448
|
@stack.push(transaction)
|
261
449
|
transaction
|
@@ -275,18 +463,35 @@ module ActiveRecord
|
|
275
463
|
@lazy_transactions_enabled
|
276
464
|
end
|
277
465
|
|
466
|
+
def dirty_current_transaction
|
467
|
+
current_transaction.dirty!
|
468
|
+
end
|
469
|
+
|
470
|
+
def restore_transactions
|
471
|
+
return false unless restorable?
|
472
|
+
|
473
|
+
@stack.each(&:restore!)
|
474
|
+
|
475
|
+
true
|
476
|
+
end
|
477
|
+
|
478
|
+
def restorable?
|
479
|
+
@stack.none?(&:dirty?)
|
480
|
+
end
|
481
|
+
|
278
482
|
def materialize_transactions
|
279
483
|
return if @materializing_transactions
|
280
|
-
return unless @has_unmaterialized_transactions
|
281
484
|
|
282
|
-
@
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
485
|
+
if @has_unmaterialized_transactions
|
486
|
+
@connection.lock.synchronize do
|
487
|
+
begin
|
488
|
+
@materializing_transactions = true
|
489
|
+
@stack.each { |t| t.materialize! unless t.materialized? }
|
490
|
+
ensure
|
491
|
+
@materializing_transactions = false
|
492
|
+
end
|
493
|
+
@has_unmaterialized_transactions = false
|
288
494
|
end
|
289
|
-
@has_unmaterialized_transactions = false
|
290
495
|
end
|
291
496
|
end
|
292
497
|
|
@@ -300,6 +505,8 @@ module ActiveRecord
|
|
300
505
|
@stack.pop
|
301
506
|
end
|
302
507
|
|
508
|
+
dirty_current_transaction if transaction.dirty?
|
509
|
+
|
303
510
|
transaction.commit
|
304
511
|
transaction.commit_records
|
305
512
|
end
|
@@ -307,8 +514,12 @@ module ActiveRecord
|
|
307
514
|
|
308
515
|
def rollback_transaction(transaction = nil)
|
309
516
|
@connection.lock.synchronize do
|
310
|
-
transaction ||= @stack.
|
311
|
-
|
517
|
+
transaction ||= @stack.last
|
518
|
+
begin
|
519
|
+
transaction.rollback
|
520
|
+
ensure
|
521
|
+
@stack.pop if @stack.last == transaction
|
522
|
+
end
|
312
523
|
transaction.rollback_records
|
313
524
|
end
|
314
525
|
end
|
@@ -316,39 +527,53 @@ module ActiveRecord
|
|
316
527
|
def within_new_transaction(isolation: nil, joinable: true)
|
317
528
|
@connection.lock.synchronize do
|
318
529
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
transaction.state.invalidate! if error.is_a? ActiveRecord::TransactionRollbackError
|
530
|
+
begin
|
531
|
+
ret = yield
|
532
|
+
completed = true
|
533
|
+
ret
|
534
|
+
rescue Exception => error
|
325
535
|
rollback_transaction
|
326
536
|
after_failure_actions(transaction, error)
|
327
|
-
end
|
328
537
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
#
|
334
|
-
#
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
538
|
+
raise
|
539
|
+
ensure
|
540
|
+
unless error
|
541
|
+
# In 7.1 we enforce timeout >= 0.4.0 which no longer use throw, so we can
|
542
|
+
# go back to the original behavior of committing on non-local return.
|
543
|
+
# If users are using throw, we assume it's not an error case.
|
544
|
+
completed = true if ActiveRecord.commit_transaction_on_non_local_return
|
545
|
+
|
546
|
+
if Thread.current.status == "aborting"
|
547
|
+
rollback_transaction
|
548
|
+
elsif !completed && transaction.written
|
549
|
+
ActiveRecord.deprecator.warn(<<~EOW)
|
550
|
+
A transaction is being rolled back because the transaction block was
|
551
|
+
exited using `return`, `break` or `throw`.
|
552
|
+
In Rails 7.2 this transaction will be committed instead.
|
553
|
+
To opt-in to the new behavior now and suppress this warning
|
554
|
+
you can set:
|
555
|
+
|
556
|
+
Rails.application.config.active_record.commit_transaction_on_non_local_return = true
|
557
|
+
EOW
|
558
|
+
rollback_transaction
|
559
|
+
else
|
560
|
+
begin
|
561
|
+
commit_transaction
|
562
|
+
rescue ActiveRecord::ConnectionFailed
|
563
|
+
transaction.invalidate! unless transaction.state.completed?
|
564
|
+
raise
|
565
|
+
rescue Exception
|
566
|
+
rollback_transaction(transaction) unless transaction.state.completed?
|
567
|
+
raise
|
568
|
+
end
|
349
569
|
end
|
350
570
|
end
|
351
571
|
end
|
572
|
+
ensure
|
573
|
+
unless transaction&.state&.completed?
|
574
|
+
@connection.throw_away!
|
575
|
+
transaction&.incomplete!
|
576
|
+
end
|
352
577
|
end
|
353
578
|
end
|
354
579
|
|