activerecord 7.1.3.3 → 7.2.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 +507 -2128
- 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 +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- 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 +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- 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 +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- 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 +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- 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 +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- 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 +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- 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 +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- 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 +6 -0
- 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 +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- 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 +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- 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 +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- 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 +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -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 +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- 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/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- 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 +500 -66
- data/lib/active_record/result.rb +32 -45
- 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/signed_id.rb +11 -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 +70 -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 +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -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 +68 -0
- data/lib/active_record/transactions.rb +43 -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 +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- 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 +8 -3
- 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/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +16 -11
@@ -115,15 +115,20 @@ module ActiveRecord
|
|
115
115
|
def dirty!; end
|
116
116
|
def invalidated?; false; end
|
117
117
|
def invalidate!; end
|
118
|
+
def materialized?; false; end
|
119
|
+
def before_commit; yield; end
|
120
|
+
def after_commit; yield; end
|
121
|
+
def after_rollback; end # noop
|
118
122
|
end
|
119
123
|
|
120
|
-
class Transaction # :nodoc:
|
124
|
+
class Transaction < ActiveRecord::Transaction # :nodoc:
|
121
125
|
attr_reader :connection, :state, :savepoint_name, :isolation_level
|
122
126
|
attr_accessor :written
|
123
127
|
|
124
128
|
delegate :invalidate!, :invalidated?, to: :@state
|
125
129
|
|
126
130
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
131
|
+
super()
|
127
132
|
@connection = connection
|
128
133
|
@state = TransactionState.new
|
129
134
|
@records = nil
|
@@ -190,60 +195,76 @@ module ActiveRecord
|
|
190
195
|
end
|
191
196
|
|
192
197
|
def rollback_records
|
193
|
-
|
194
|
-
|
195
|
-
|
198
|
+
if records
|
199
|
+
begin
|
200
|
+
ite = unique_records
|
196
201
|
|
197
|
-
|
202
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
198
203
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
204
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
205
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
206
|
+
end
|
207
|
+
ensure
|
208
|
+
ite&.each do |i|
|
209
|
+
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
|
210
|
+
end
|
211
|
+
end
|
205
212
|
end
|
213
|
+
|
214
|
+
@callbacks&.each(&:after_rollback)
|
206
215
|
end
|
207
216
|
|
208
217
|
def before_commit_records
|
209
|
-
return unless records
|
210
|
-
|
211
218
|
if @run_commit_callbacks
|
212
|
-
if
|
213
|
-
|
219
|
+
if records
|
220
|
+
if ActiveRecord.before_committed_on_all_records
|
221
|
+
ite = unique_records
|
214
222
|
|
215
|
-
|
216
|
-
|
217
|
-
|
223
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
224
|
+
candidates[record] = record
|
225
|
+
end
|
218
226
|
|
219
|
-
|
220
|
-
|
227
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
228
|
+
record.before_committed! if should_run_callbacks
|
229
|
+
end
|
230
|
+
else
|
231
|
+
records.uniq.each(&:before_committed!)
|
221
232
|
end
|
222
|
-
else
|
223
|
-
records.uniq.each(&:before_committed!)
|
224
233
|
end
|
234
|
+
|
235
|
+
@callbacks&.each(&:before_commit)
|
225
236
|
end
|
237
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
238
|
+
# remaining callbacks to the parent transaction
|
226
239
|
end
|
227
240
|
|
228
241
|
def commit_records
|
229
|
-
|
230
|
-
|
231
|
-
|
242
|
+
if records
|
243
|
+
begin
|
244
|
+
ite = unique_records
|
232
245
|
|
233
|
-
|
234
|
-
|
246
|
+
if @run_commit_callbacks
|
247
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
235
248
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
249
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
250
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
251
|
+
end
|
252
|
+
else
|
253
|
+
while record = ite.shift
|
254
|
+
# if not running callbacks, only adds the record to the parent transaction
|
255
|
+
connection.add_transaction_record(record)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
ensure
|
259
|
+
ite&.each { |i| i.committed!(should_run_callbacks: false) }
|
243
260
|
end
|
244
261
|
end
|
245
|
-
|
246
|
-
|
262
|
+
|
263
|
+
if @run_commit_callbacks
|
264
|
+
@callbacks&.each(&:after_commit)
|
265
|
+
elsif @callbacks
|
266
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
267
|
+
end
|
247
268
|
end
|
248
269
|
|
249
270
|
def full_rollback?; true; end
|
@@ -349,7 +370,7 @@ module ActiveRecord
|
|
349
370
|
|
350
371
|
def rollback
|
351
372
|
unless @state.invalidated?
|
352
|
-
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
373
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
353
374
|
end
|
354
375
|
@state.rollback!
|
355
376
|
@instrumenter.finish(:rollback) if materialized?
|
@@ -532,9 +553,7 @@ module ActiveRecord
|
|
532
553
|
@connection.lock.synchronize do
|
533
554
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
534
555
|
begin
|
535
|
-
|
536
|
-
completed = true
|
537
|
-
ret
|
556
|
+
yield transaction
|
538
557
|
rescue Exception => error
|
539
558
|
rollback_transaction
|
540
559
|
after_failure_actions(transaction, error)
|
@@ -542,24 +561,8 @@ module ActiveRecord
|
|
542
561
|
raise
|
543
562
|
ensure
|
544
563
|
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
564
|
if Thread.current.status == "aborting"
|
551
565
|
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
566
|
else
|
564
567
|
begin
|
565
568
|
commit_transaction
|
@@ -590,7 +593,7 @@ module ActiveRecord
|
|
590
593
|
end
|
591
594
|
|
592
595
|
private
|
593
|
-
NULL_TRANSACTION = NullTransaction.new
|
596
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
594
597
|
|
595
598
|
# Deallocate invalidated prepared statements outside of the transaction
|
596
599
|
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,13 @@ module ActiveRecord
|
|
174
172
|
@verified = false
|
175
173
|
end
|
176
174
|
|
177
|
-
THREAD_LOCK = ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
178
|
-
private_constant :THREAD_LOCK
|
179
|
-
|
180
|
-
FIBER_LOCK = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
181
|
-
private_constant :FIBER_LOCK
|
182
|
-
|
183
175
|
def lock_thread=(lock_thread) # :nodoc:
|
184
176
|
@lock =
|
185
177
|
case lock_thread
|
186
178
|
when Thread
|
187
|
-
|
179
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
188
180
|
when Fiber
|
189
|
-
|
181
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
190
182
|
else
|
191
183
|
ActiveSupport::Concurrency::NullLock
|
192
184
|
end
|
@@ -215,10 +207,6 @@ module ActiveRecord
|
|
215
207
|
@config[:replica] || false
|
216
208
|
end
|
217
209
|
|
218
|
-
def use_metadata_table?
|
219
|
-
@config.fetch(:use_metadata_table, true)
|
220
|
-
end
|
221
|
-
|
222
210
|
def connection_retries
|
223
211
|
(@config[:connection_retries] || 1).to_i
|
224
212
|
end
|
@@ -246,22 +234,6 @@ module ActiveRecord
|
|
246
234
|
connection_class.current_preventing_writes
|
247
235
|
end
|
248
236
|
|
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
237
|
def prepared_statements?
|
266
238
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
267
239
|
end
|
@@ -327,7 +299,7 @@ module ActiveRecord
|
|
327
299
|
end
|
328
300
|
|
329
301
|
def schema_cache
|
330
|
-
@schema_cache ||= BoundSchemaReflection.
|
302
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
331
303
|
end
|
332
304
|
|
333
305
|
# this method must only be called while holding connection pool's mutex
|
@@ -580,7 +552,7 @@ module ActiveRecord
|
|
580
552
|
end
|
581
553
|
|
582
554
|
def return_value_after_insert?(column) # :nodoc:
|
583
|
-
column.
|
555
|
+
column.auto_populated?
|
584
556
|
end
|
585
557
|
|
586
558
|
def async_enabled? # :nodoc:
|
@@ -651,15 +623,6 @@ module ActiveRecord
|
|
651
623
|
yield
|
652
624
|
end
|
653
625
|
|
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
626
|
# Override to check all foreign key constraints in a database.
|
664
627
|
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
665
628
|
# constraints are not met.
|
@@ -668,6 +631,13 @@ module ActiveRecord
|
|
668
631
|
|
669
632
|
# CONNECTION MANAGEMENT ====================================
|
670
633
|
|
634
|
+
# Checks whether the connection to the database was established. This doesn't
|
635
|
+
# include checking whether the database is actually capable of responding, i.e.
|
636
|
+
# whether the connection is stale.
|
637
|
+
def connected?
|
638
|
+
!@raw_connection.nil?
|
639
|
+
end
|
640
|
+
|
671
641
|
# Checks whether the connection to the database is still active. This includes
|
672
642
|
# checking whether the database is actually capable of responding, i.e. whether
|
673
643
|
# the connection isn't stale.
|
@@ -711,13 +681,14 @@ module ActiveRecord
|
|
711
681
|
end
|
712
682
|
end
|
713
683
|
|
714
|
-
|
715
684
|
# Disconnects from the database if already connected. Otherwise, this
|
716
685
|
# method does nothing.
|
717
686
|
def disconnect!
|
718
|
-
|
719
|
-
|
720
|
-
|
687
|
+
@lock.synchronize do
|
688
|
+
clear_cache!(new_connection: true)
|
689
|
+
reset_transaction
|
690
|
+
@raw_connection_dirty = false
|
691
|
+
end
|
721
692
|
end
|
722
693
|
|
723
694
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -773,19 +744,17 @@ module ActiveRecord
|
|
773
744
|
# is no longer active, then this method will reconnect to the database.
|
774
745
|
def verify!
|
775
746
|
unless active?
|
776
|
-
|
777
|
-
@
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
return
|
784
|
-
end
|
747
|
+
@lock.synchronize do
|
748
|
+
if @unconfigured_connection
|
749
|
+
@raw_connection = @unconfigured_connection
|
750
|
+
@unconfigured_connection = nil
|
751
|
+
configure_connection
|
752
|
+
@verified = true
|
753
|
+
return
|
785
754
|
end
|
786
|
-
end
|
787
755
|
|
788
|
-
|
756
|
+
reconnect!(restore_transactions: true)
|
757
|
+
end
|
789
758
|
end
|
790
759
|
|
791
760
|
@verified = true
|
@@ -868,7 +837,7 @@ module ActiveRecord
|
|
868
837
|
end
|
869
838
|
|
870
839
|
def database_version # :nodoc:
|
871
|
-
|
840
|
+
pool.server_version(self)
|
872
841
|
end
|
873
842
|
|
874
843
|
def check_version # :nodoc:
|
@@ -879,7 +848,7 @@ module ActiveRecord
|
|
879
848
|
# numbered migration that has been executed, or 0 if no schema
|
880
849
|
# information is present / the database is empty.
|
881
850
|
def schema_version
|
882
|
-
migration_context.current_version
|
851
|
+
pool.migration_context.current_version
|
883
852
|
end
|
884
853
|
|
885
854
|
class << self
|
@@ -1149,6 +1118,7 @@ module ActiveRecord
|
|
1149
1118
|
statement_name: statement_name,
|
1150
1119
|
async: async,
|
1151
1120
|
connection: self,
|
1121
|
+
row_count: 0,
|
1152
1122
|
&block
|
1153
1123
|
)
|
1154
1124
|
rescue ActiveRecord::StatementInvalid => ex
|
@@ -1212,7 +1182,7 @@ module ActiveRecord
|
|
1212
1182
|
#
|
1213
1183
|
# This is an internal hook to make possible connection adapters to build
|
1214
1184
|
# custom result objects with connection-specific data.
|
1215
|
-
def build_result(columns:, rows:, column_types:
|
1185
|
+
def build_result(columns:, rows:, column_types: nil)
|
1216
1186
|
ActiveRecord::Result.new(columns, rows, column_types)
|
1217
1187
|
end
|
1218
1188
|
|
@@ -1224,6 +1194,7 @@ module ActiveRecord
|
|
1224
1194
|
# Implementations may assume this method will only be called while
|
1225
1195
|
# holding @lock (or from #initialize).
|
1226
1196
|
def configure_connection
|
1197
|
+
check_version
|
1227
1198
|
end
|
1228
1199
|
|
1229
1200
|
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:
|
@@ -336,7 +340,7 @@ module ActiveRecord
|
|
336
340
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
337
341
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
338
342
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
339
|
-
rename_table_indexes(table_name, new_name)
|
343
|
+
rename_table_indexes(table_name, new_name, **options)
|
340
344
|
end
|
341
345
|
|
342
346
|
# Drops a table from the database.
|
@@ -635,27 +639,59 @@ module ActiveRecord
|
|
635
639
|
end
|
636
640
|
|
637
641
|
def build_insert_sql(insert) # :nodoc:
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
sql
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
642
|
+
no_op_column = quote_column_name(insert.keys.first)
|
643
|
+
|
644
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
645
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
646
|
+
if supports_insert_raw_alias_syntax?
|
647
|
+
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
648
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
649
|
+
|
650
|
+
if insert.skip_duplicates?
|
651
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
652
|
+
elsif insert.update_duplicates?
|
653
|
+
if insert.raw_update_sql?
|
654
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
655
|
+
else
|
656
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
657
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
658
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
659
|
+
end
|
660
|
+
end
|
661
|
+
else
|
662
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
663
|
+
|
664
|
+
if insert.skip_duplicates?
|
665
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
666
|
+
elsif insert.update_duplicates?
|
667
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
668
|
+
if insert.raw_update_sql?
|
669
|
+
sql << insert.raw_update_sql
|
670
|
+
else
|
671
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
672
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
673
|
+
end
|
650
674
|
end
|
651
675
|
end
|
652
676
|
|
677
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
653
678
|
sql
|
654
679
|
end
|
655
680
|
|
656
681
|
def check_version # :nodoc:
|
657
682
|
if database_version < "5.5.8"
|
658
|
-
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
683
|
+
raise DatabaseVersionError, "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
#--
|
688
|
+
# QUOTING ==================================================
|
689
|
+
#++
|
690
|
+
|
691
|
+
# Quotes strings for use in SQL input.
|
692
|
+
def quote_string(string)
|
693
|
+
with_raw_connection(allow_retry: true, materialize_transactions: false) do |connection|
|
694
|
+
connection.escape(string)
|
659
695
|
end
|
660
696
|
end
|
661
697
|
|
@@ -734,7 +770,11 @@ module ActiveRecord
|
|
734
770
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
735
771
|
|
736
772
|
@affected_rows_before_warnings = @raw_connection.affected_rows
|
773
|
+
warning_count = @raw_connection.warning_count
|
737
774
|
result = @raw_connection.query("SHOW WARNINGS")
|
775
|
+
result = [
|
776
|
+
["Warning", nil, "Query had warning_count=#{warning_count} but ‘SHOW WARNINGS’ did not return the warnings. Check MySQL logs or database configuration."],
|
777
|
+
] if result.count == 0
|
738
778
|
result.each do |level, code, message|
|
739
779
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
740
780
|
next if warning_ignored?(warning)
|
@@ -756,6 +796,7 @@ module ActiveRecord
|
|
756
796
|
ER_DB_CREATE_EXISTS = 1007
|
757
797
|
ER_FILSORT_ABORT = 1028
|
758
798
|
ER_DUP_ENTRY = 1062
|
799
|
+
ER_SERVER_SHUTDOWN = 1053
|
759
800
|
ER_NOT_NULL_VIOLATION = 1048
|
760
801
|
ER_NO_REFERENCED_ROW = 1216
|
761
802
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -784,7 +825,7 @@ module ActiveRecord
|
|
784
825
|
else
|
785
826
|
super
|
786
827
|
end
|
787
|
-
when ER_CONNECTION_KILLED, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
828
|
+
when ER_CONNECTION_KILLED, ER_SERVER_SHUTDOWN, CR_SERVER_GONE_ERROR, CR_SERVER_LOST, ER_CLIENT_INTERACTION_TIMEOUT
|
788
829
|
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
789
830
|
when ER_DB_CREATE_EXISTS
|
790
831
|
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
@@ -853,6 +894,10 @@ module ActiveRecord
|
|
853
894
|
"DROP INDEX #{quote_column_name(index_name)}"
|
854
895
|
end
|
855
896
|
|
897
|
+
def supports_insert_raw_alias_syntax?
|
898
|
+
!mariadb? && database_version >= "8.0.19"
|
899
|
+
end
|
900
|
+
|
856
901
|
def supports_rename_index?
|
857
902
|
if mariadb?
|
858
903
|
database_version >= "10.5.2"
|
@@ -870,6 +915,7 @@ module ActiveRecord
|
|
870
915
|
end
|
871
916
|
|
872
917
|
def configure_connection
|
918
|
+
super
|
873
919
|
variables = @config.fetch(:variables, {}).stringify_keys
|
874
920
|
|
875
921
|
# Increase timeout so the server doesn't disconnect us.
|
@@ -977,7 +1023,11 @@ module ActiveRecord
|
|
977
1023
|
end
|
978
1024
|
|
979
1025
|
def version_string(full_version_string)
|
980
|
-
full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1026
|
+
if full_version_string && matches = full_version_string.match(/^(?:5\.5\.5-)?(\d+\.\d+\.\d+)/)
|
1027
|
+
matches[1]
|
1028
|
+
else
|
1029
|
+
raise DatabaseVersionError, "Unable to parse MySQL version from #{full_version_string.inspect}"
|
1030
|
+
end
|
981
1031
|
end
|
982
1032
|
end
|
983
1033
|
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
|