activerecord 7.1.3.4 → 7.2.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +514 -2126
- 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 +161 -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 +65 -60
- data/lib/active_record/connection_adapters/abstract_adapter.rb +33 -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 +197 -44
- 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 +131 -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 +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 +17 -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
|
@@ -107,6 +109,7 @@ module ActiveRecord
|
|
107
109
|
def initialize; end
|
108
110
|
def state; end
|
109
111
|
def closed?; true; end
|
112
|
+
alias_method :blank?, :closed?
|
110
113
|
def open?; false; end
|
111
114
|
def joinable?; false; end
|
112
115
|
def add_record(record, _ = true); end
|
@@ -115,15 +118,21 @@ 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 # noop
|
125
|
+
def uuid; Digest::UUID.nil_uuid; end
|
118
126
|
end
|
119
127
|
|
120
|
-
class Transaction # :nodoc:
|
128
|
+
class Transaction < ActiveRecord::Transaction # :nodoc:
|
121
129
|
attr_reader :connection, :state, :savepoint_name, :isolation_level
|
122
130
|
attr_accessor :written
|
123
131
|
|
124
132
|
delegate :invalidate!, :invalidated?, to: :@state
|
125
133
|
|
126
134
|
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
135
|
+
super()
|
127
136
|
@connection = connection
|
128
137
|
@state = TransactionState.new
|
129
138
|
@records = nil
|
@@ -133,7 +142,7 @@ module ActiveRecord
|
|
133
142
|
@run_commit_callbacks = run_commit_callbacks
|
134
143
|
@lazy_enrollment_records = nil
|
135
144
|
@dirty = false
|
136
|
-
@instrumenter = TransactionInstrumenter.new(connection: connection)
|
145
|
+
@instrumenter = TransactionInstrumenter.new(connection: connection, transaction: self)
|
137
146
|
end
|
138
147
|
|
139
148
|
def dirty!
|
@@ -190,66 +199,80 @@ module ActiveRecord
|
|
190
199
|
end
|
191
200
|
|
192
201
|
def rollback_records
|
193
|
-
|
194
|
-
|
195
|
-
|
202
|
+
if records
|
203
|
+
begin
|
204
|
+
ite = unique_records
|
196
205
|
|
197
|
-
|
206
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
198
207
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
208
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
209
|
+
record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
|
210
|
+
end
|
211
|
+
ensure
|
212
|
+
ite&.each do |i|
|
213
|
+
i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
|
214
|
+
end
|
215
|
+
end
|
205
216
|
end
|
217
|
+
|
218
|
+
@callbacks&.each(&:after_rollback)
|
206
219
|
end
|
207
220
|
|
208
221
|
def before_commit_records
|
209
|
-
return unless records
|
210
|
-
|
211
222
|
if @run_commit_callbacks
|
212
|
-
if
|
213
|
-
|
223
|
+
if records
|
224
|
+
if ActiveRecord.before_committed_on_all_records
|
225
|
+
ite = unique_records
|
214
226
|
|
215
|
-
|
216
|
-
|
217
|
-
|
227
|
+
instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
|
228
|
+
candidates[record] = record
|
229
|
+
end
|
218
230
|
|
219
|
-
|
220
|
-
|
231
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
232
|
+
record.before_committed! if should_run_callbacks
|
233
|
+
end
|
234
|
+
else
|
235
|
+
records.uniq.each(&:before_committed!)
|
221
236
|
end
|
222
|
-
else
|
223
|
-
records.uniq.each(&:before_committed!)
|
224
237
|
end
|
238
|
+
|
239
|
+
@callbacks&.each(&:before_commit)
|
225
240
|
end
|
241
|
+
# Note: When @run_commit_callbacks is false #commit_records takes care of appending
|
242
|
+
# remaining callbacks to the parent transaction
|
226
243
|
end
|
227
244
|
|
228
245
|
def commit_records
|
229
|
-
|
230
|
-
|
231
|
-
|
246
|
+
if records
|
247
|
+
begin
|
248
|
+
ite = unique_records
|
232
249
|
|
233
|
-
|
234
|
-
|
250
|
+
if @run_commit_callbacks
|
251
|
+
instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
|
235
252
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
253
|
+
run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
|
254
|
+
record.committed!(should_run_callbacks: should_run_callbacks)
|
255
|
+
end
|
256
|
+
else
|
257
|
+
while record = ite.shift
|
258
|
+
# if not running callbacks, only adds the record to the parent transaction
|
259
|
+
connection.add_transaction_record(record)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
ensure
|
263
|
+
ite&.each { |i| i.committed!(should_run_callbacks: false) }
|
243
264
|
end
|
244
265
|
end
|
245
|
-
|
246
|
-
|
266
|
+
|
267
|
+
if @run_commit_callbacks
|
268
|
+
@callbacks&.each(&:after_commit)
|
269
|
+
elsif @callbacks
|
270
|
+
connection.current_transaction.append_callbacks(@callbacks)
|
271
|
+
end
|
247
272
|
end
|
248
273
|
|
249
274
|
def full_rollback?; true; end
|
250
275
|
def joinable?; @joinable; end
|
251
|
-
def closed?; false; end
|
252
|
-
def open?; !closed?; end
|
253
276
|
|
254
277
|
private
|
255
278
|
def unique_records
|
@@ -349,7 +372,7 @@ module ActiveRecord
|
|
349
372
|
|
350
373
|
def rollback
|
351
374
|
unless @state.invalidated?
|
352
|
-
connection.rollback_to_savepoint(savepoint_name) if materialized?
|
375
|
+
connection.rollback_to_savepoint(savepoint_name) if materialized? && connection.active?
|
353
376
|
end
|
354
377
|
@state.rollback!
|
355
378
|
@instrumenter.finish(:rollback) if materialized?
|
@@ -532,9 +555,7 @@ module ActiveRecord
|
|
532
555
|
@connection.lock.synchronize do
|
533
556
|
transaction = begin_transaction(isolation: isolation, joinable: joinable)
|
534
557
|
begin
|
535
|
-
|
536
|
-
completed = true
|
537
|
-
ret
|
558
|
+
yield transaction
|
538
559
|
rescue Exception => error
|
539
560
|
rollback_transaction
|
540
561
|
after_failure_actions(transaction, error)
|
@@ -542,24 +563,8 @@ module ActiveRecord
|
|
542
563
|
raise
|
543
564
|
ensure
|
544
565
|
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
566
|
if Thread.current.status == "aborting"
|
551
567
|
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
568
|
else
|
564
569
|
begin
|
565
570
|
commit_transaction
|
@@ -590,7 +595,7 @@ module ActiveRecord
|
|
590
595
|
end
|
591
596
|
|
592
597
|
private
|
593
|
-
NULL_TRANSACTION = NullTransaction.new
|
598
|
+
NULL_TRANSACTION = NullTransaction.new.freeze
|
594
599
|
|
595
600
|
# Deallocate invalidated prepared statements outside of the transaction
|
596
601
|
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,8 @@ module ActiveRecord
|
|
1149
1118
|
statement_name: statement_name,
|
1150
1119
|
async: async,
|
1151
1120
|
connection: self,
|
1121
|
+
transaction: current_transaction.presence,
|
1122
|
+
row_count: 0,
|
1152
1123
|
&block
|
1153
1124
|
)
|
1154
1125
|
rescue ActiveRecord::StatementInvalid => ex
|
@@ -1212,7 +1183,7 @@ module ActiveRecord
|
|
1212
1183
|
#
|
1213
1184
|
# This is an internal hook to make possible connection adapters to build
|
1214
1185
|
# custom result objects with connection-specific data.
|
1215
|
-
def build_result(columns:, rows:, column_types:
|
1186
|
+
def build_result(columns:, rows:, column_types: nil)
|
1216
1187
|
ActiveRecord::Result.new(columns, rows, column_types)
|
1217
1188
|
end
|
1218
1189
|
|
@@ -1224,6 +1195,7 @@ module ActiveRecord
|
|
1224
1195
|
# Implementations may assume this method will only be called while
|
1225
1196
|
# holding @lock (or from #initialize).
|
1226
1197
|
def configure_connection
|
1198
|
+
check_version
|
1227
1199
|
end
|
1228
1200
|
|
1229
1201
|
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
|