activerecord 7.1.4.1 → 7.2.2.1
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 +643 -2274
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +7 -1
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +4 -4
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +59 -292
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods.rb +54 -63
- data/lib/active_record/attributes.rb +61 -47
- data/lib/active_record/autosave_association.rb +12 -29
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +189 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +15 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +40 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +17 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +86 -38
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +3 -3
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +19 -2
- data/lib/active_record/errors.rb +46 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +8 -4
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +32 -68
- data/lib/active_record/nested_attributes.rb +24 -5
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +42 -57
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +40 -43
- data/lib/active_record/reflection.rb +98 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +96 -63
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +224 -58
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/signed_id.rb +20 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +81 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +86 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +70 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +15 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +148 -39
- data/lib/arel/alias_predication.rb +1 -1
- 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/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -12
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
# = Active Record Connection Adapters Transaction State
|
@@ -89,7 +91,9 @@ module ActiveRecord
|
|
89
91
|
raise InstrumentationAlreadyStartedError.new("Called start on an already started transaction") if @started
|
90
92
|
@started = true
|
91
93
|
|
92
|
-
|
94
|
+
ActiveSupport::Notifications.instrument("start_transaction.active_record", @base_payload)
|
95
|
+
|
96
|
+
@payload = @base_payload.dup # We dup because the payload for a given event is mutated later to add the outcome.
|
93
97
|
@handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload)
|
94
98
|
@handle.start
|
95
99
|
end
|
@@ -104,7 +108,6 @@ module ActiveRecord
|
|
104
108
|
end
|
105
109
|
|
106
110
|
class NullTransaction # :nodoc:
|
107
|
-
def initialize; end
|
108
111
|
def state; end
|
109
112
|
def closed?; true; end
|
110
113
|
def open?; false; end
|
@@ -115,17 +118,43 @@ module ActiveRecord
|
|
115
118
|
def dirty!; end
|
116
119
|
def invalidated?; false; end
|
117
120
|
def invalidate!; end
|
121
|
+
def materialized?; false; end
|
122
|
+
def before_commit; yield; end
|
123
|
+
def after_commit; yield; end
|
124
|
+
def after_rollback; end
|
125
|
+
def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
|
118
126
|
end
|
119
127
|
|
120
128
|
class Transaction # :nodoc:
|
121
|
-
|
129
|
+
class Callback # :nodoc:
|
130
|
+
def initialize(event, callback)
|
131
|
+
@event = event
|
132
|
+
@callback = callback
|
133
|
+
end
|
134
|
+
|
135
|
+
def before_commit
|
136
|
+
@callback.call if @event == :before_commit
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_commit
|
140
|
+
@callback.call if @event == :after_commit
|
141
|
+
end
|
142
|
+
|
143
|
+
def after_rollback
|
144
|
+
@callback.call if @event == :after_rollback
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
attr_reader :connection, :state, :savepoint_name, :isolation_level, :user_transaction
|
122
149
|
attr_accessor :written
|
123
150
|
|
124
151
|
delegate :invalidate!, :invalidated?, to: :@state
|
125
152
|
|
126
153
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
154
|
+
super()
|
127
155
|
@connection = connection
|
128
156
|
@state = TransactionState.new
|
157
|
+
@callbacks = nil
|
129
158
|
@records = nil
|
130
159
|
@isolation_level = isolation
|
131
160
|
@materialized = false
|
@@ -133,7 +162,8 @@ module ActiveRecord
|
|
133
162
|
@run_commit_callbacks = run_commit_callbacks
|
134
163
|
@lazy_enrollment_records = nil
|
135
164
|
@dirty = false
|
136
|
-
@
|
165
|
+
@user_transaction = joinable ? ActiveRecord::Transaction.new(self) : ActiveRecord::Transaction::NULL_TRANSACTION
|
166
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: @user_transaction)
|
137
167
|
end
|
138
168
|
|
139
169
|
def dirty!
|
@@ -144,6 +174,14 @@ module ActiveRecord
|
|
144
174
|
@dirty
|
145
175
|
end
|
146
176
|
|
177
|
+
def open?
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
def closed?
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
147
185
|
def add_record(record, ensure_finalize = true)
|
148
186
|
@records ||= []
|
149
187
|
if ensure_finalize
|
@@ -154,6 +192,30 @@ module ActiveRecord
|
|
154
192
|
end
|
155
193
|
end
|
156
194
|
|
195
|
+
def before_commit(&block)
|
196
|
+
if @state.finalized?
|
197
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
198
|
+
end
|
199
|
+
|
200
|
+
(@callbacks ||= []) << Callback.new(:before_commit, block)
|
201
|
+
end
|
202
|
+
|
203
|
+
def after_commit(&block)
|
204
|
+
if @state.finalized?
|
205
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
206
|
+
end
|
207
|
+
|
208
|
+
(@callbacks ||= []) << Callback.new(:after_commit, block)
|
209
|
+
end
|
210
|
+
|
211
|
+
def after_rollback(&block)
|
212
|
+
if @state.finalized?
|
213
|
+
raise ActiveRecordError, "Cannot register callbacks on a finalized transaction"
|
214
|
+
end
|
215
|
+
|
216
|
+
(@callbacks ||= []) << Callback.new(:after_rollback, block)
|
217
|
+
end
|
218
|
+
|
157
219
|
def records
|
158
220
|
if @lazy_enrollment_records
|
159
221
|
@records.concat @lazy_enrollment_records.values
|
@@ -190,66 +252,85 @@ module ActiveRecord
|
|
190
252
|
end
|
191
253
|
|
192
254
|
def rollback_records
|
193
|
-
|
194
|
-
|
195
|
-
|
255
|
+
if records
|
256
|
+
begin
|
257
|
+
ite = unique_records
|
196
258
|
|
197
|
-
|
259
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
198
260
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
261
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
262
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
263
|
+
end
|
264
|
+
ensure
|
265
|
+
ite&.each do |i|
|
266
|
+
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
|
267
|
+
end
|
268
|
+
end
|
205
269
|
end
|
270
|
+
|
271
|
+
@callbacks&.each(&:after_rollback)
|
206
272
|
end
|
207
273
|
|
208
274
|
def before_commit_records
|
209
|
-
return unless records
|
210
|
-
|
211
275
|
if @run_commit_callbacks
|
212
|
-
if
|
213
|
-
|
276
|
+
if records
|
277
|
+
if ActiveRecord.before_committed_on_all_records
|
278
|
+
ite = unique_records
|
214
279
|
|
215
|
-
|
216
|
-
|
217
|
-
|
280
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
281
|
+
candidates[record] = record
|
282
|
+
end
|
218
283
|
|
219
|
-
|
220
|
-
|
284
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
285
|
+
record.before_committed! if should_run_callbacks
|
286
|
+
end
|
287
|
+
else
|
288
|
+
records.uniq.each(&:before_committed!)
|
221
289
|
end
|
222
|
-
else
|
223
|
-
records.uniq.each(&:before_committed!)
|
224
290
|
end
|
291
|
+
|
292
|
+
@callbacks&.each(&:before_commit)
|
225
293
|
end
|
294
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
295
|
+
# remaining callbacks to the parent transaction
|
226
296
|
end
|
227
297
|
|
228
298
|
def commit_records
|
229
|
-
|
230
|
-
|
231
|
-
|
299
|
+
if records
|
300
|
+
begin
|
301
|
+
ite = unique_records
|
232
302
|
|
233
|
-
|
234
|
-
|
303
|
+
if @run_commit_callbacks
|
304
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
235
305
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
306
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
307
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
308
|
+
end
|
309
|
+
else
|
310
|
+
while record = ite.shift
|
311
|
+
# if not running callbacks, only adds the record to the parent transaction
|
312
|
+
connection.add_transaction_record(record)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
ensure
|
316
|
+
ite&.each { |i| i.committed!(should_run_callbacks: false) }
|
243
317
|
end
|
244
318
|
end
|
245
|
-
|
246
|
-
|
319
|
+
|
320
|
+
if @run_commit_callbacks
|
321
|
+
@callbacks&.each(&:after_commit)
|
322
|
+
elsif @callbacks
|
323
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
324
|
+
end
|
247
325
|
end
|
248
326
|
|
249
327
|
def full_rollback?; true; end
|
250
328
|
def joinable?; @joinable; end
|
251
|
-
|
252
|
-
|
329
|
+
|
330
|
+
protected
|
331
|
+
def append_callbacks(callbacks) # :nodoc:
|
332
|
+
(@callbacks ||= []).concat(callbacks)
|
333
|
+
end
|
253
334
|
|
254
335
|
private
|
255
336
|
def unique_records
|
@@ -349,7 +430,7 @@ module ActiveRecord
|
|
349
430
|
|
350
431
|
def rollback
|
351
432
|
unless @state.invalidated?
|
352
|
-
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
433
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
353
434
|
end
|
354
435
|
@state.rollback!
|
355
436
|
@instrumenter.finish(:rollback) if materialized?
|
@@ -532,9 +613,7 @@ module ActiveRecord
|
|
532
613
|
@connection.lock.synchronize do
|
533
614
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
534
615
|
begin
|
535
|
-
|
536
|
-
completed = true
|
537
|
-
ret
|
616
|
+
yield transaction.user_transaction
|
538
617
|
rescue Exception => error
|
539
618
|
rollback_transaction
|
540
619
|
after_failure_actions(transaction, error)
|
@@ -542,24 +621,8 @@ module ActiveRecord
|
|
542
621
|
raise
|
543
622
|
ensure
|
544
623
|
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
624
|
if Thread.current.status == "aborting"
|
551
625
|
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
626
|
else
|
564
627
|
begin
|
565
628
|
commit_transaction
|
@@ -590,7 +653,7 @@ module ActiveRecord
|
|
590
653
|
end
|
591
654
|
|
592
655
|
private
|
593
|
-
NULL_TRANSACTION = NullTransaction.new
|
656
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
594
657
|
|
595
658
|
# Deallocate invalidated prepared statements outside of the transaction
|
596
659
|
def after_failure_actions(transaction, error)
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
# and +:limit+ options, etc.
|
24
24
|
#
|
25
25
|
# All the concrete database adapters follow the interface laid down in this class.
|
26
|
-
# {ActiveRecord::Base.
|
26
|
+
# {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
|
27
27
|
# you can use.
|
28
28
|
#
|
29
29
|
# Most of the methods in the adapter are useful during migrations. Most
|
@@ -49,8 +49,6 @@ module ActiveRecord
|
|
49
49
|
return if value.eql?(@pool)
|
50
50
|
@schema_cache = nil
|
51
51
|
@pool = value
|
52
|
-
|
53
|
-
@pool.schema_reflection.load!(self) if ActiveRecord.lazily_load_schema_cache
|
54
52
|
end
|
55
53
|
|
56
54
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
@@ -136,7 +134,7 @@ module ActiveRecord
|
|
136
134
|
@logger = ActiveRecord::Base.logger
|
137
135
|
|
138
136
|
if deprecated_logger || deprecated_connection_options || deprecated_config
|
139
|
-
raise ArgumentError, "when initializing an
|
137
|
+
raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
|
140
138
|
end
|
141
139
|
else
|
142
140
|
# Soft-deprecated for now; we'll probably warn in future.
|
@@ -174,19 +172,20 @@ module ActiveRecord
|
|
174
172
|
@verified = false
|
175
173
|
end
|
176
174
|
|
177
|
-
|
178
|
-
|
175
|
+
def inspect # :nodoc:
|
176
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
177
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
179
178
|
|
180
|
-
|
181
|
-
|
179
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
180
|
+
end
|
182
181
|
|
183
182
|
def lock_thread=(lock_thread) # :nodoc:
|
184
183
|
@lock =
|
185
184
|
case lock_thread
|
186
185
|
when Thread
|
187
|
-
|
186
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
188
187
|
when Fiber
|
189
|
-
|
188
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
190
189
|
else
|
191
190
|
ActiveSupport::Concurrency::NullLock
|
192
191
|
end
|
@@ -215,10 +214,6 @@ module ActiveRecord
|
|
215
214
|
@config[:replica] || false
|
216
215
|
end
|
217
216
|
|
218
|
-
def use_metadata_table?
|
219
|
-
@config.fetch(:use_metadata_table, true)
|
220
|
-
end
|
221
|
-
|
222
217
|
def connection_retries
|
223
218
|
(@config[:connection_retries] || 1).to_i
|
224
219
|
end
|
@@ -246,22 +241,6 @@ module ActiveRecord
|
|
246
241
|
connection_class.current_preventing_writes
|
247
242
|
end
|
248
243
|
|
249
|
-
def migrations_paths # :nodoc:
|
250
|
-
@config[:migrations_paths] || Migrator.migrations_paths
|
251
|
-
end
|
252
|
-
|
253
|
-
def migration_context # :nodoc:
|
254
|
-
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
255
|
-
end
|
256
|
-
|
257
|
-
def schema_migration # :nodoc:
|
258
|
-
SchemaMigration.new(self)
|
259
|
-
end
|
260
|
-
|
261
|
-
def internal_metadata # :nodoc:
|
262
|
-
InternalMetadata.new(self)
|
263
|
-
end
|
264
|
-
|
265
244
|
def prepared_statements?
|
266
245
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
267
246
|
end
|
@@ -327,7 +306,7 @@ module ActiveRecord
|
|
327
306
|
end
|
328
307
|
|
329
308
|
def schema_cache
|
330
|
-
@schema_cache ||= BoundSchemaReflection.
|
309
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
331
310
|
end
|
332
311
|
|
333
312
|
# this method must only be called while holding connection pool's mutex
|
@@ -580,7 +559,7 @@ module ActiveRecord
|
|
580
559
|
end
|
581
560
|
|
582
561
|
def return_value_after_insert?(column) # :nodoc:
|
583
|
-
column.
|
562
|
+
column.auto_populated?
|
584
563
|
end
|
585
564
|
|
586
565
|
def async_enabled? # :nodoc:
|
@@ -651,15 +630,6 @@ module ActiveRecord
|
|
651
630
|
yield
|
652
631
|
end
|
653
632
|
|
654
|
-
# Override to check all foreign key constraints in a database.
|
655
|
-
def all_foreign_keys_valid?
|
656
|
-
check_all_foreign_keys_valid!
|
657
|
-
true
|
658
|
-
rescue ActiveRecord::StatementInvalid
|
659
|
-
false
|
660
|
-
end
|
661
|
-
deprecate :all_foreign_keys_valid?, deprecator: ActiveRecord.deprecator
|
662
|
-
|
663
633
|
# Override to check all foreign key constraints in a database.
|
664
634
|
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
665
635
|
# constraints are not met.
|
@@ -668,6 +638,13 @@ module ActiveRecord
|
|
668
638
|
|
669
639
|
# CONNECTION MANAGEMENT ====================================
|
670
640
|
|
641
|
+
# Checks whether the connection to the database was established. This doesn't
|
642
|
+
# include checking whether the database is actually capable of responding, i.e.
|
643
|
+
# whether the connection is stale.
|
644
|
+
def connected?
|
645
|
+
!@raw_connection.nil?
|
646
|
+
end
|
647
|
+
|
671
648
|
# Checks whether the connection to the database is still active. This includes
|
672
649
|
# checking whether the database is actually capable of responding, i.e. whether
|
673
650
|
# the connection isn't stale.
|
@@ -867,7 +844,7 @@ module ActiveRecord
|
|
867
844
|
end
|
868
845
|
|
869
846
|
def database_version # :nodoc:
|
870
|
-
|
847
|
+
pool.server_version(self)
|
871
848
|
end
|
872
849
|
|
873
850
|
def check_version # :nodoc:
|
@@ -878,7 +855,7 @@ module ActiveRecord
|
|
878
855
|
# numbered migration that has been executed, or 0 if no schema
|
879
856
|
# information is present / the database is empty.
|
880
857
|
def schema_version
|
881
|
-
migration_context.current_version
|
858
|
+
pool.migration_context.current_version
|
882
859
|
end
|
883
860
|
|
884
861
|
class << self
|
@@ -1148,6 +1125,8 @@ module ActiveRecord
|
|
1148
1125
|
statement_name: statement_name,
|
1149
1126
|
async: async,
|
1150
1127
|
connection: self,
|
1128
|
+
transaction: current_transaction.user_transaction.presence,
|
1129
|
+
row_count: 0,
|
1151
1130
|
&block
|
1152
1131
|
)
|
1153
1132
|
rescue ActiveRecord::StatementInvalid => ex
|
@@ -1211,7 +1190,7 @@ module ActiveRecord
|
|
1211
1190
|
#
|
1212
1191
|
# This is an internal hook to make possible connection adapters to build
|
1213
1192
|
# custom result objects with connection-specific data.
|
1214
|
-
def build_result(columns:, rows:, column_types:
|
1193
|
+
def build_result(columns:, rows:, column_types: nil)
|
1215
1194
|
ActiveRecord::Result.new(columns, rows, column_types)
|
1216
1195
|
end
|
1217
1196
|
|
@@ -1223,6 +1202,7 @@ module ActiveRecord
|
|
1223
1202
|
# Implementations may assume this method will only be called while
|
1224
1203
|
# holding @lock (or from #initialize).
|
1225
1204
|
def configure_connection
|
1205
|
+
check_version
|
1226
1206
|
end
|
1227
1207
|
|
1228
1208
|
def default_prepared_statements
|
@@ -170,6 +170,10 @@ module ActiveRecord
|
|
170
170
|
true
|
171
171
|
end
|
172
172
|
|
173
|
+
def supports_insert_returning?
|
174
|
+
mariadb? && database_version >= "10.5.0"
|
175
|
+
end
|
176
|
+
|
173
177
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
174
178
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
175
179
|
end
|
@@ -214,7 +218,7 @@ module ActiveRecord
|
|
214
218
|
update("SET FOREIGN_KEY_CHECKS = 0")
|
215
219
|
yield
|
216
220
|
ensure
|
217
|
-
update("SET FOREIGN_KEY_CHECKS = #{old}")
|
221
|
+
update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
|
218
222
|
end
|
219
223
|
end
|
220
224
|
|
@@ -225,12 +229,12 @@ module ActiveRecord
|
|
225
229
|
# Mysql2Adapter doesn't have to free a result after using it, but we use this method
|
226
230
|
# to write stuff in an abstract way without concerning ourselves about whether it
|
227
231
|
# needs to be explicitly freed or not.
|
228
|
-
def execute_and_free(sql, name = nil, async: false) # :nodoc:
|
232
|
+
def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
|
229
233
|
sql = transform_query(sql)
|
230
234
|
check_if_write_query(sql)
|
231
235
|
|
232
236
|
mark_transaction_written_if_write(sql)
|
233
|
-
yield raw_execute(sql, name, async: async)
|
237
|
+
yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
|
234
238
|
end
|
235
239
|
|
236
240
|
def begin_db_transaction # :nodoc:
|
@@ -635,16 +639,18 @@ module ActiveRecord
|
|
635
639
|
end
|
636
640
|
|
637
641
|
def build_insert_sql(insert) # :nodoc:
|
638
|
-
no_op_column = quote_column_name(insert.keys.first)
|
642
|
+
no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
|
639
643
|
|
640
644
|
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
641
645
|
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
642
646
|
if supports_insert_raw_alias_syntax?
|
643
|
-
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
|
644
648
|
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
645
649
|
|
646
650
|
if insert.skip_duplicates?
|
647
|
-
|
651
|
+
if no_op_column
|
652
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
653
|
+
end
|
648
654
|
elsif insert.update_duplicates?
|
649
655
|
if insert.raw_update_sql?
|
650
656
|
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
@@ -658,7 +664,9 @@ module ActiveRecord
|
|
658
664
|
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
659
665
|
|
660
666
|
if insert.skip_duplicates?
|
661
|
-
|
667
|
+
if no_op_column
|
668
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
669
|
+
end
|
662
670
|
elsif insert.update_duplicates?
|
663
671
|
sql << " ON DUPLICATE KEY UPDATE "
|
664
672
|
if insert.raw_update_sql?
|
@@ -670,12 +678,24 @@ module ActiveRecord
|
|
670
678
|
end
|
671
679
|
end
|
672
680
|
|
681
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
673
682
|
sql
|
674
683
|
end
|
675
684
|
|
676
685
|
def check_version # :nodoc:
|
677
686
|
if database_version < "5.5.8"
|
678
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
687
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
#--
|
692
|
+
# QUOTING ==================================================
|
693
|
+
#++
|
694
|
+
|
695
|
+
# Quotes strings for use in SQL input.
|
696
|
+
def quote_string(string)
|
697
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
698
|
+
connection.escape(string)
|
679
699
|
end
|
680
700
|
end
|
681
701
|
|
@@ -754,7 +774,11 @@ module ActiveRecord
|
|
754
774
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
755
775
|
|
756
776
|
@affected_rows_before_warnings = @raw_connection.affected_rows
|
777
|
+
warning_count = @raw_connection.warning_count
|
757
778
|
result = @raw_connection.query("SHOW WARNINGS")
|
779
|
+
result = [
|
780
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
781
|
+
] if result.count == 0
|
758
782
|
result.each do |level, code, message|
|
759
783
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
760
784
|
next if warning_ignored?(warning)
|
@@ -776,6 +800,7 @@ module ActiveRecord
|
|
776
800
|
ER_DB_CREATE_EXISTS = 1007
|
777
801
|
ER_FILSORT_ABORT = 1028
|
778
802
|
ER_DUP_ENTRY = 1062
|
803
|
+
ER_SERVER_SHUTDOWN = 1053
|
779
804
|
ER_NOT_NULL_VIOLATION = 1048
|
780
805
|
ER_NO_REFERENCED_ROW = 1216
|
781
806
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -804,7 +829,7 @@ module ActiveRecord
|
|
804
829
|
else
|
805
830
|
super
|
806
831
|
end
|
807
|
-
when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
832
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
808
833
|
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
809
834
|
when ER_DB_CREATE_EXISTS
|
810
835
|
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
@@ -894,6 +919,7 @@ module ActiveRecord
|
|
894
919
|
end
|
895
920
|
|
896
921
|
def configure_connection
|
922
|
+
super
|
897
923
|
variables = @config.fetch(:variables, {}).stringify_keys
|
898
924
|
|
899
925
|
# Increase timeout so the server doesn't disconnect us.
|
@@ -1001,7 +1027,11 @@ module ActiveRecord
|
|
1001
1027
|
end
|
1002
1028
|
|
1003
1029
|
def version_string(full_version_string)
|
1004
|
-
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1030
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1031
|
+
matches[1]
|
1032
|
+
else
|
1033
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
1034
|
+
end
|
1005
1035
|
end
|
1006
1036
|
end
|
1007
1037
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_current-timestamp
|
13
13
|
# https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-syntax.html
|
14
|
-
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)").freeze # :nodoc:
|
14
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP(6)", retryable: true).freeze # :nodoc:
|
15
15
|
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
16
16
|
|
17
17
|
def write_query?(sql) # :nodoc:
|
@@ -55,6 +55,14 @@ module ActiveRecord
|
|
55
55
|
super unless column.auto_increment?
|
56
56
|
end
|
57
57
|
|
58
|
+
def returning_column_values(result)
|
59
|
+
if supports_insert_returning?
|
60
|
+
result.rows.first
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
58
66
|
def combine_multi_statements(total_sql)
|
59
67
|
total_sql.each_with_object([]) do |sql, total_sql_chunks|
|
60
68
|
previous_packet = total_sql_chunks.last
|