activerecord 7.2.1.1 → 8.0.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 +220 -756
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +25 -5
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -4
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -12
- data/lib/active_record/attributes.rb +1 -2
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +26 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -42
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +54 -14
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +28 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +15 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +54 -75
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- data/lib/active_record/model_schema.rb +4 -6
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +0 -4
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +9 -38
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +23 -23
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +41 -40
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +14 -1
- data/lib/active_record/relation/query_methods.rb +122 -71
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +40 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +9 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +10 -11
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -221,8 +221,10 @@ module ActiveRecord
|
|
221
221
|
if reflection.validate? && !method_defined?(validation_method)
|
222
222
|
if reflection.collection?
|
223
223
|
method = :validate_collection_association
|
224
|
+
elsif reflection.has_one?
|
225
|
+
method = :validate_has_one_association
|
224
226
|
else
|
225
|
-
method = :
|
227
|
+
method = :validate_belongs_to_association
|
226
228
|
end
|
227
229
|
|
228
230
|
define_non_cyclic_method(validation_method) { send(method, reflection) }
|
@@ -274,6 +276,16 @@ module ActiveRecord
|
|
274
276
|
new_record? || has_changes_to_save? || marked_for_destruction? || nested_records_changed_for_autosave?
|
275
277
|
end
|
276
278
|
|
279
|
+
def validating_belongs_to_for?(association)
|
280
|
+
@validating_belongs_to_for ||= {}
|
281
|
+
@validating_belongs_to_for[association]
|
282
|
+
end
|
283
|
+
|
284
|
+
def autosaving_belongs_to_for?(association)
|
285
|
+
@autosaving_belongs_to_for ||= {}
|
286
|
+
@autosaving_belongs_to_for[association]
|
287
|
+
end
|
288
|
+
|
277
289
|
private
|
278
290
|
def init_internals
|
279
291
|
super
|
@@ -313,11 +325,33 @@ module ActiveRecord
|
|
313
325
|
end
|
314
326
|
|
315
327
|
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
|
316
|
-
# turned on for the association.
|
317
|
-
def
|
328
|
+
# turned on for the has_one association.
|
329
|
+
def validate_has_one_association(reflection)
|
330
|
+
association = association_instance_get(reflection.name)
|
331
|
+
record = association && association.reader
|
332
|
+
return unless record && (record.changed_for_autosave? || custom_validation_context?)
|
333
|
+
|
334
|
+
inverse_association = reflection.inverse_of && record.association(reflection.inverse_of.name)
|
335
|
+
return if inverse_association && (record.validating_belongs_to_for?(inverse_association) ||
|
336
|
+
record.autosaving_belongs_to_for?(inverse_association))
|
337
|
+
|
338
|
+
association_valid?(association, record)
|
339
|
+
end
|
340
|
+
|
341
|
+
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
|
342
|
+
# turned on for the belongs_to association.
|
343
|
+
def validate_belongs_to_association(reflection)
|
318
344
|
association = association_instance_get(reflection.name)
|
319
345
|
record = association && association.reader
|
320
|
-
|
346
|
+
return unless record && (record.changed_for_autosave? || custom_validation_context?)
|
347
|
+
|
348
|
+
begin
|
349
|
+
@validating_belongs_to_for ||= {}
|
350
|
+
@validating_belongs_to_for[association] = true
|
351
|
+
association_valid?(association, record)
|
352
|
+
ensure
|
353
|
+
@validating_belongs_to_for[association] = false
|
354
|
+
end
|
321
355
|
end
|
322
356
|
|
323
357
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -431,33 +465,34 @@ module ActiveRecord
|
|
431
465
|
return unless association && association.loaded?
|
432
466
|
|
433
467
|
record = association.load_target
|
468
|
+
return unless record && !record.destroyed?
|
434
469
|
|
435
|
-
|
436
|
-
autosave = reflection.options[:autosave]
|
437
|
-
|
438
|
-
if autosave && record.marked_for_destruction?
|
439
|
-
record.destroy
|
440
|
-
elsif autosave != false
|
441
|
-
primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
|
442
|
-
primary_key_value = primary_key.map { |key| _read_attribute(key) }
|
470
|
+
autosave = reflection.options[:autosave]
|
443
471
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
472
|
+
if autosave && record.marked_for_destruction?
|
473
|
+
record.destroy
|
474
|
+
elsif autosave != false
|
475
|
+
primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
|
476
|
+
primary_key_value = primary_key.map { |key| _read_attribute(key) }
|
477
|
+
return unless (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
|
448
478
|
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
end
|
453
|
-
association.set_inverse_instance(record)
|
454
|
-
end
|
479
|
+
unless reflection.through_reflection
|
480
|
+
foreign_key = Array(reflection.foreign_key)
|
481
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
455
482
|
|
456
|
-
|
457
|
-
|
458
|
-
|
483
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
484
|
+
association_id = _read_attribute(primary_key)
|
485
|
+
record[foreign_key] = association_id unless record[foreign_key] == association_id
|
459
486
|
end
|
487
|
+
association.set_inverse_instance(record)
|
460
488
|
end
|
489
|
+
|
490
|
+
inverse_association = reflection.inverse_of && record.association(reflection.inverse_of.name)
|
491
|
+
return if inverse_association && record.autosaving_belongs_to_for?(inverse_association)
|
492
|
+
|
493
|
+
saved = record.save(validate: !autosave)
|
494
|
+
raise ActiveRecord::Rollback if !saved && autosave
|
495
|
+
saved
|
461
496
|
end
|
462
497
|
end
|
463
498
|
|
@@ -482,7 +517,6 @@ module ActiveRecord
|
|
482
517
|
return false unless reflection.inverse_of&.polymorphic?
|
483
518
|
|
484
519
|
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
|
485
|
-
|
486
520
|
reflection.active_record != record.class.polymorphic_class_for(class_name)
|
487
521
|
end
|
488
522
|
|
@@ -502,7 +536,15 @@ module ActiveRecord
|
|
502
536
|
foreign_key.each { |key| self[key] = nil }
|
503
537
|
record.destroy
|
504
538
|
elsif autosave != false
|
505
|
-
saved =
|
539
|
+
saved = if record.new_record? || (autosave && record.changed_for_autosave?)
|
540
|
+
begin
|
541
|
+
@autosaving_belongs_to_for ||= {}
|
542
|
+
@autosaving_belongs_to_for[association] = true
|
543
|
+
record.save(validate: !autosave)
|
544
|
+
ensure
|
545
|
+
@autosaving_belongs_to_for[association] = false
|
546
|
+
end
|
547
|
+
end
|
506
548
|
|
507
549
|
if association.updated?
|
508
550
|
primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
|
@@ -418,7 +418,7 @@ module ActiveRecord
|
|
418
418
|
|
419
419
|
def destroy # :nodoc:
|
420
420
|
@_destroy_callback_already_called ||= false
|
421
|
-
return if @_destroy_callback_already_called
|
421
|
+
return true if @_destroy_callback_already_called
|
422
422
|
@_destroy_callback_already_called = true
|
423
423
|
_run_destroy_callbacks { super }
|
424
424
|
rescue RecordNotDestroyed => e
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "thread"
|
4
3
|
require "concurrent/map"
|
5
4
|
|
6
5
|
module ActiveRecord
|
@@ -210,18 +209,25 @@ module ActiveRecord
|
|
210
209
|
# This makes retrieving the connection pool O(1) once the process is warm.
|
211
210
|
# When a connection is established or removed, we invalidate the cache.
|
212
211
|
def retrieve_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard, strict: false)
|
213
|
-
|
212
|
+
pool_manager = get_pool_manager(connection_name)
|
213
|
+
pool = pool_manager&.get_pool_config(role, shard)&.pool
|
214
214
|
|
215
215
|
if strict && !pool
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
216
|
+
selector = [
|
217
|
+
("'#{shard}' shard" unless shard == ActiveRecord::Base.default_shard),
|
218
|
+
("'#{role}' role" unless role == ActiveRecord::Base.default_role),
|
219
|
+
].compact.join(" and ")
|
220
|
+
|
221
|
+
selector = [
|
222
|
+
(connection_name unless connection_name == "ActiveRecord::Base"),
|
223
|
+
selector.presence,
|
224
|
+
].compact.join(" with ")
|
225
|
+
|
226
|
+
selector = " for #{selector}" if selector.present?
|
227
|
+
|
228
|
+
message = "No database connection defined#{selector}."
|
223
229
|
|
224
|
-
raise
|
230
|
+
raise ConnectionNotDefined.new(message, connection_name: connection_name, shard: shard, role: role)
|
225
231
|
end
|
226
232
|
|
227
233
|
pool
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "thread"
|
4
3
|
require "concurrent/map"
|
5
4
|
require "monitor"
|
6
5
|
|
@@ -184,6 +183,31 @@ module ActiveRecord
|
|
184
183
|
end
|
185
184
|
end
|
186
185
|
|
186
|
+
module ExecutorHooks # :nodoc:
|
187
|
+
class << self
|
188
|
+
def run
|
189
|
+
# noop
|
190
|
+
end
|
191
|
+
|
192
|
+
def complete(_)
|
193
|
+
ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
|
194
|
+
if (connection = pool.active_connection?)
|
195
|
+
transaction = connection.current_transaction
|
196
|
+
if transaction.closed? || !transaction.joinable?
|
197
|
+
pool.release_connection
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class << self
|
206
|
+
def install_executor_hooks(executor = ActiveSupport::Executor)
|
207
|
+
executor.register_hook(ExecutorHooks)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
187
211
|
include MonitorMixin
|
188
212
|
prepend QueryCache::ConnectionPoolConfiguration
|
189
213
|
include ConnectionAdapters::AbstractPool
|
@@ -292,14 +316,6 @@ module ActiveRecord
|
|
292
316
|
connection_lease.sticky.nil?
|
293
317
|
end
|
294
318
|
|
295
|
-
def connection
|
296
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
297
|
-
ActiveRecord::ConnectionAdapters::ConnectionPool#connection is deprecated
|
298
|
-
and will be removed in Rails 8.0. Use #lease_connection instead.
|
299
|
-
MSG
|
300
|
-
lease_connection
|
301
|
-
end
|
302
|
-
|
303
319
|
def pin_connection!(lock_thread) # :nodoc:
|
304
320
|
@pinned_connection ||= (connection_lease&.connection || checkout)
|
305
321
|
@pinned_connections_depth += 1
|
@@ -333,6 +349,7 @@ module ActiveRecord
|
|
333
349
|
end
|
334
350
|
|
335
351
|
if @pinned_connection.nil?
|
352
|
+
connection.steal!
|
336
353
|
connection.lock_thread = nil
|
337
354
|
checkin(connection)
|
338
355
|
end
|
@@ -102,16 +102,16 @@ module ActiveRecord
|
|
102
102
|
select_all(arel, name, binds, async: async).then(&:rows)
|
103
103
|
end
|
104
104
|
|
105
|
-
def query_value(
|
106
|
-
single_value_from_rows(query(
|
105
|
+
def query_value(...) # :nodoc:
|
106
|
+
single_value_from_rows(query(...))
|
107
107
|
end
|
108
108
|
|
109
|
-
def query_values(
|
110
|
-
query(
|
109
|
+
def query_values(...) # :nodoc:
|
110
|
+
query(...).map(&:first)
|
111
111
|
end
|
112
112
|
|
113
|
-
def query(
|
114
|
-
internal_exec_query(
|
113
|
+
def query(...) # :nodoc:
|
114
|
+
internal_exec_query(...).rows
|
115
115
|
end
|
116
116
|
|
117
117
|
# Determines whether the SQL statement is a write query.
|
@@ -163,14 +163,14 @@ module ActiveRecord
|
|
163
163
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
164
164
|
# the executed +sql+ statement.
|
165
165
|
def exec_delete(sql, name = nil, binds = [])
|
166
|
-
|
166
|
+
affected_rows(internal_execute(sql, name, binds))
|
167
167
|
end
|
168
168
|
|
169
169
|
# Executes update +sql+ statement in the context of this connection using
|
170
170
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
171
171
|
# the executed +sql+ statement.
|
172
172
|
def exec_update(sql, name = nil, binds = [])
|
173
|
-
|
173
|
+
affected_rows(internal_execute(sql, name, binds))
|
174
174
|
end
|
175
175
|
|
176
176
|
def exec_insert_all(sql, name) # :nodoc:
|
@@ -224,11 +224,9 @@ module ActiveRecord
|
|
224
224
|
|
225
225
|
return if table_names.empty?
|
226
226
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
execute_batch(statements, "Truncate Tables")
|
231
|
-
end
|
227
|
+
disable_referential_integrity do
|
228
|
+
statements = build_truncate_statements(table_names)
|
229
|
+
execute_batch(statements, "Truncate Tables")
|
232
230
|
end
|
233
231
|
end
|
234
232
|
|
@@ -358,7 +356,7 @@ module ActiveRecord
|
|
358
356
|
end
|
359
357
|
yield current_transaction.user_transaction
|
360
358
|
else
|
361
|
-
|
359
|
+
within_new_transaction(isolation: isolation, joinable: joinable, &block)
|
362
360
|
end
|
363
361
|
rescue ActiveRecord::Rollback
|
364
362
|
# rollbacks are silently swallowed
|
@@ -411,6 +409,14 @@ module ActiveRecord
|
|
411
409
|
# Begins the transaction (and turns off auto-committing).
|
412
410
|
def begin_db_transaction() end
|
413
411
|
|
412
|
+
def begin_deferred_transaction(isolation_level = nil) # :nodoc:
|
413
|
+
if isolation_level
|
414
|
+
begin_isolated_db_transaction(isolation_level)
|
415
|
+
else
|
416
|
+
begin_db_transaction
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
414
420
|
def transaction_isolation_levels
|
415
421
|
{
|
416
422
|
read_uncommitted: "READ UNCOMMITTED",
|
@@ -427,6 +433,15 @@ module ActiveRecord
|
|
427
433
|
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
428
434
|
end
|
429
435
|
|
436
|
+
# Hook point called after an isolated DB transaction is committed
|
437
|
+
# or rolled back.
|
438
|
+
# Most adapters don't need to implement anything because the isolation
|
439
|
+
# level is set on a per transaction basis.
|
440
|
+
# But some databases like SQLite set it on a per connection level
|
441
|
+
# and need to explicitly reset it after commit or rollback.
|
442
|
+
def reset_isolation_level
|
443
|
+
end
|
444
|
+
|
430
445
|
# Commits the transaction (and turns on auto-committing).
|
431
446
|
def commit_db_transaction() end
|
432
447
|
|
@@ -473,11 +488,9 @@ module ActiveRecord
|
|
473
488
|
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
|
474
489
|
statements = table_deletes + fixture_inserts
|
475
490
|
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
execute_batch(statements, "Fixtures Load")
|
480
|
-
end
|
491
|
+
transaction(requires_new: true) do
|
492
|
+
disable_referential_integrity do
|
493
|
+
execute_batch(statements, "Fixtures Load")
|
481
494
|
end
|
482
495
|
end
|
483
496
|
end
|
@@ -524,28 +537,64 @@ module ActiveRecord
|
|
524
537
|
HIGH_PRECISION_CURRENT_TIMESTAMP
|
525
538
|
end
|
526
539
|
|
527
|
-
|
528
|
-
|
540
|
+
# Same as raw_execute but returns an ActiveRecord::Result object.
|
541
|
+
def raw_exec_query(...) # :nodoc:
|
542
|
+
cast_result(raw_execute(...))
|
543
|
+
end
|
544
|
+
|
545
|
+
# Execute a query and returns an ActiveRecord::Result
|
546
|
+
def internal_exec_query(...) # :nodoc:
|
547
|
+
cast_result(internal_execute(...))
|
529
548
|
end
|
530
549
|
|
531
550
|
private
|
532
|
-
|
533
|
-
|
534
|
-
|
551
|
+
# Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
|
552
|
+
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
|
553
|
+
type_casted_binds = type_casted_binds(binds)
|
554
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
555
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
556
|
+
perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
end
|
535
560
|
|
536
|
-
|
561
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
|
562
|
+
raise NotImplementedError
|
563
|
+
end
|
537
564
|
|
538
|
-
|
565
|
+
# Receive a native adapter result object and returns an ActiveRecord::Result object.
|
566
|
+
def cast_result(raw_result)
|
567
|
+
raise NotImplementedError
|
539
568
|
end
|
540
569
|
|
541
|
-
def
|
542
|
-
|
543
|
-
|
570
|
+
def affected_rows(raw_result)
|
571
|
+
raise NotImplementedError
|
572
|
+
end
|
573
|
+
|
574
|
+
def preprocess_query(sql)
|
575
|
+
check_if_write_query(sql)
|
576
|
+
mark_transaction_written_if_write(sql)
|
577
|
+
|
578
|
+
# We call tranformers after the write checks so we don't add extra parsing work.
|
579
|
+
# This means we assume no transformer whille change a read for a write
|
580
|
+
# but it would be insane to do such a thing.
|
581
|
+
ActiveRecord.query_transformers.each do |transformer|
|
582
|
+
sql = transformer.call(sql, self)
|
544
583
|
end
|
584
|
+
|
585
|
+
sql
|
545
586
|
end
|
546
587
|
|
547
|
-
|
548
|
-
|
588
|
+
# Same as #internal_exec_query, but yields a native adapter result
|
589
|
+
def internal_execute(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, &block)
|
590
|
+
sql = preprocess_query(sql)
|
591
|
+
raw_execute(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions, &block)
|
592
|
+
end
|
593
|
+
|
594
|
+
def execute_batch(statements, name = nil, **kwargs)
|
595
|
+
statements.each do |statement|
|
596
|
+
raw_execute(statement, name, **kwargs)
|
597
|
+
end
|
549
598
|
end
|
550
599
|
|
551
600
|
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
@@ -614,10 +663,6 @@ module ActiveRecord
|
|
614
663
|
end
|
615
664
|
end
|
616
665
|
|
617
|
-
def with_multi_statements
|
618
|
-
yield
|
619
|
-
end
|
620
|
-
|
621
666
|
def combine_multi_statements(total_sql)
|
622
667
|
total_sql.join(";\n")
|
623
668
|
end
|
@@ -629,6 +674,8 @@ module ActiveRecord
|
|
629
674
|
raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
|
630
675
|
end
|
631
676
|
|
677
|
+
# We make sure to run query transformers on the orignal thread
|
678
|
+
sql = preprocess_query(sql)
|
632
679
|
future_result = async.new(
|
633
680
|
pool,
|
634
681
|
sql,
|
@@ -636,19 +683,19 @@ module ActiveRecord
|
|
636
683
|
binds,
|
637
684
|
prepare: prepare,
|
638
685
|
)
|
639
|
-
if supports_concurrent_connections? && current_transaction.
|
686
|
+
if supports_concurrent_connections? && !current_transaction.joinable?
|
640
687
|
future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
|
641
688
|
else
|
642
689
|
future_result.execute!(self)
|
643
690
|
end
|
644
|
-
|
645
|
-
end
|
646
|
-
|
647
|
-
result = internal_exec_query(sql, name, binds, prepare: prepare, allow_retry: allow_retry)
|
648
|
-
if async
|
649
|
-
FutureResult.wrap(result)
|
691
|
+
future_result
|
650
692
|
else
|
651
|
-
result
|
693
|
+
result = internal_exec_query(sql, name, binds, prepare: prepare, allow_retry: allow_retry)
|
694
|
+
if async
|
695
|
+
FutureResult.wrap(result)
|
696
|
+
else
|
697
|
+
result
|
698
|
+
end
|
652
699
|
end
|
653
700
|
end
|
654
701
|
|
@@ -157,11 +157,13 @@ module ActiveRecord
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def enable_query_cache!
|
160
|
-
query_cache.enabled
|
160
|
+
query_cache.enabled = true
|
161
|
+
query_cache.dirties = true
|
161
162
|
end
|
162
163
|
|
163
164
|
def disable_query_cache!
|
164
|
-
query_cache.enabled
|
165
|
+
query_cache.enabled = false
|
166
|
+
query_cache.dirties = true
|
165
167
|
end
|
166
168
|
|
167
169
|
def query_cache_enabled
|
@@ -266,7 +268,7 @@ module ActiveRecord
|
|
266
268
|
if result
|
267
269
|
ActiveSupport::Notifications.instrument(
|
268
270
|
"sql.active_record",
|
269
|
-
|
271
|
+
cache_notification_info_result(sql, name, binds, result)
|
270
272
|
)
|
271
273
|
end
|
272
274
|
|
@@ -288,13 +290,19 @@ module ActiveRecord
|
|
288
290
|
if hit
|
289
291
|
ActiveSupport::Notifications.instrument(
|
290
292
|
"sql.active_record",
|
291
|
-
|
293
|
+
cache_notification_info_result(sql, name, binds, result)
|
292
294
|
)
|
293
295
|
end
|
294
296
|
|
295
297
|
result.dup
|
296
298
|
end
|
297
299
|
|
300
|
+
def cache_notification_info_result(sql, name, binds, result)
|
301
|
+
payload = cache_notification_info(sql, name, binds)
|
302
|
+
payload[:row_count] = result.length
|
303
|
+
payload
|
304
|
+
end
|
305
|
+
|
298
306
|
# Database adapters can override this method to
|
299
307
|
# provide custom cache information.
|
300
308
|
def cache_notification_info(sql, name, binds)
|
@@ -28,6 +28,7 @@ module ActiveRecord
|
|
28
28
|
sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
|
29
29
|
sql << o.check_constraint_adds.map { |con| visit_AddCheckConstraint con }.join(" ")
|
30
30
|
sql << o.check_constraint_drops.map { |con| visit_DropCheckConstraint con }.join(" ")
|
31
|
+
sql << o.constraint_drops.map { |con| visit_DropConstraint con }.join(" ")
|
31
32
|
end
|
32
33
|
|
33
34
|
def visit_ColumnDefinition(o)
|
@@ -96,9 +97,11 @@ module ActiveRecord
|
|
96
97
|
"ADD #{accept(o)}"
|
97
98
|
end
|
98
99
|
|
99
|
-
def
|
100
|
+
def visit_DropConstraint(name)
|
100
101
|
"DROP CONSTRAINT #{quote_column_name(name)}"
|
101
102
|
end
|
103
|
+
alias :visit_DropForeignKey :visit_DropConstraint
|
104
|
+
alias :visit_DropCheckConstraint :visit_DropConstraint
|
102
105
|
|
103
106
|
def visit_CreateIndexDefinition(o)
|
104
107
|
index = o.index
|
@@ -127,10 +130,6 @@ module ActiveRecord
|
|
127
130
|
"ADD #{accept(o)}"
|
128
131
|
end
|
129
132
|
|
130
|
-
def visit_DropCheckConstraint(name)
|
131
|
-
"DROP CONSTRAINT #{quote_column_name(name)}"
|
132
|
-
end
|
133
|
-
|
134
133
|
def quoted_columns(o)
|
135
134
|
String === o.columns ? o.columns : quoted_columns_for_index(o.columns, o.column_options)
|
136
135
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
3
|
module ActiveRecord
|
5
4
|
module ConnectionAdapters # :nodoc:
|
6
5
|
# Abstract representation of an index definition on a table. Instances of
|
@@ -348,7 +347,7 @@ module ActiveRecord
|
|
348
347
|
# Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
|
349
348
|
# is actually of this type:
|
350
349
|
#
|
351
|
-
# class SomeMigration < ActiveRecord::Migration[
|
350
|
+
# class SomeMigration < ActiveRecord::Migration[8.0]
|
352
351
|
# def up
|
353
352
|
# create_table :foo do |t|
|
354
353
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
@@ -622,6 +621,7 @@ module ActiveRecord
|
|
622
621
|
attr_reader :adds
|
623
622
|
attr_reader :foreign_key_adds, :foreign_key_drops
|
624
623
|
attr_reader :check_constraint_adds, :check_constraint_drops
|
624
|
+
attr_reader :constraint_drops
|
625
625
|
|
626
626
|
def initialize(td)
|
627
627
|
@td = td
|
@@ -630,6 +630,7 @@ module ActiveRecord
|
|
630
630
|
@foreign_key_drops = []
|
631
631
|
@check_constraint_adds = []
|
632
632
|
@check_constraint_drops = []
|
633
|
+
@constraint_drops = []
|
633
634
|
end
|
634
635
|
|
635
636
|
def name; @td.name; end
|
@@ -650,6 +651,10 @@ module ActiveRecord
|
|
650
651
|
@check_constraint_drops << constraint_name
|
651
652
|
end
|
652
653
|
|
654
|
+
def drop_constraint(constraint_name)
|
655
|
+
@constraint_drops << constraint_name
|
656
|
+
end
|
657
|
+
|
653
658
|
def add_column(name, type, **options)
|
654
659
|
name = name.to_s
|
655
660
|
type = type.to_sym
|