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