activerecord 7.1.4 → 7.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +515 -2387
- 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 +14 -7
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +6 -4
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +1 -1
- 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 +33 -16
- 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 +4 -16
- 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/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +60 -71
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +13 -32
- 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 -65
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
- 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 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/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 +15 -11
- 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 +107 -75
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +52 -36
- data/lib/active_record/counter_cache.rb +18 -9
- 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 +38 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +24 -0
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
- 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/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 +8 -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 +7 -6
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +5 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +28 -67
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +30 -352
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +48 -57
- 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 +90 -35
- 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.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -57
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +496 -72
- data/lib/active_record/result.rb +31 -44
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/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 +76 -59
- 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 +81 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +1 -1
- 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 +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- metadata +17 -12
@@ -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.
|
@@ -867,7 +837,7 @@ module ActiveRecord
|
|
867
837
|
end
|
868
838
|
|
869
839
|
def database_version # :nodoc:
|
870
|
-
|
840
|
+
pool.server_version(self)
|
871
841
|
end
|
872
842
|
|
873
843
|
def check_version # :nodoc:
|
@@ -878,7 +848,7 @@ module ActiveRecord
|
|
878
848
|
# numbered migration that has been executed, or 0 if no schema
|
879
849
|
# information is present / the database is empty.
|
880
850
|
def schema_version
|
881
|
-
migration_context.current_version
|
851
|
+
pool.migration_context.current_version
|
882
852
|
end
|
883
853
|
|
884
854
|
class << self
|
@@ -1148,6 +1118,7 @@ module ActiveRecord
|
|
1148
1118
|
statement_name: statement_name,
|
1149
1119
|
async: async,
|
1150
1120
|
connection: self,
|
1121
|
+
row_count: 0,
|
1151
1122
|
&block
|
1152
1123
|
)
|
1153
1124
|
rescue ActiveRecord::StatementInvalid => ex
|
@@ -1211,7 +1182,7 @@ module ActiveRecord
|
|
1211
1182
|
#
|
1212
1183
|
# This is an internal hook to make possible connection adapters to build
|
1213
1184
|
# custom result objects with connection-specific data.
|
1214
|
-
def build_result(columns:, rows:, column_types:
|
1185
|
+
def build_result(columns:, rows:, column_types: nil)
|
1215
1186
|
ActiveRecord::Result.new(columns, rows, column_types)
|
1216
1187
|
end
|
1217
1188
|
|
@@ -1223,6 +1194,7 @@ module ActiveRecord
|
|
1223
1194
|
# Implementations may assume this method will only be called while
|
1224
1195
|
# holding @lock (or from #initialize).
|
1225
1196
|
def configure_connection
|
1197
|
+
check_version
|
1226
1198
|
end
|
1227
1199
|
|
1228
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:
|
@@ -670,12 +674,24 @@ module ActiveRecord
|
|
670
674
|
end
|
671
675
|
end
|
672
676
|
|
677
|
+
sql << " RETURNING #{insert.returning}" if insert.returning
|
673
678
|
sql
|
674
679
|
end
|
675
680
|
|
676
681
|
def check_version # :nodoc:
|
677
682
|
if database_version < "5.5.8"
|
678
|
-
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)
|
679
695
|
end
|
680
696
|
end
|
681
697
|
|
@@ -754,7 +770,11 @@ module ActiveRecord
|
|
754
770
|
return if ActiveRecord.db_warnings_action.nil? || @raw_connection.warning_count == 0
|
755
771
|
|
756
772
|
@affected_rows_before_warnings = @raw_connection.affected_rows
|
773
|
+
warning_count = @raw_connection.warning_count
|
757
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
|
758
778
|
result.each do |level, code, message|
|
759
779
|
warning = SQLWarning.new(message, code, level, sql, @pool)
|
760
780
|
next if warning_ignored?(warning)
|
@@ -776,6 +796,7 @@ module ActiveRecord
|
|
776
796
|
ER_DB_CREATE_EXISTS = 1007
|
777
797
|
ER_FILSORT_ABORT = 1028
|
778
798
|
ER_DUP_ENTRY = 1062
|
799
|
+
ER_SERVER_SHUTDOWN = 1053
|
779
800
|
ER_NOT_NULL_VIOLATION = 1048
|
780
801
|
ER_NO_REFERENCED_ROW = 1216
|
781
802
|
ER_ROW_IS_REFERENCED = 1217
|
@@ -804,7 +825,7 @@ module ActiveRecord
|
|
804
825
|
else
|
805
826
|
super
|
806
827
|
end
|
807
|
-
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
|
808
829
|
ConnectionFailed.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
809
830
|
when ER_DB_CREATE_EXISTS
|
810
831
|
DatabaseAlreadyExists.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
@@ -894,6 +915,7 @@ module ActiveRecord
|
|
894
915
|
end
|
895
916
|
|
896
917
|
def configure_connection
|
918
|
+
super
|
897
919
|
variables = @config.fetch(:variables, {}).stringify_keys
|
898
920
|
|
899
921
|
# Increase timeout so the server doesn't disconnect us.
|
@@ -1001,7 +1023,11 @@ module ActiveRecord
|
|
1001
1023
|
end
|
1002
1024
|
|
1003
1025
|
def version_string(full_version_string)
|
1004
|
-
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
|
1005
1031
|
end
|
1006
1032
|
end
|
1007
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
|
@@ -6,9 +6,52 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
9
11
|
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
12
|
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
13
|
|
14
|
+
module ClassMethods # :nodoc:
|
15
|
+
def column_name_matcher
|
16
|
+
/
|
17
|
+
\A
|
18
|
+
(
|
19
|
+
(?:
|
20
|
+
# `table_name`.`column_name` | function(one or no argument)
|
21
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
22
|
+
)
|
23
|
+
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
24
|
+
)
|
25
|
+
(?:\s*,\s*\g<1>)*
|
26
|
+
\z
|
27
|
+
/ix
|
28
|
+
end
|
29
|
+
|
30
|
+
def column_name_with_order_matcher
|
31
|
+
/
|
32
|
+
\A
|
33
|
+
(
|
34
|
+
(?:
|
35
|
+
# `table_name`.`column_name` | function(one or no argument)
|
36
|
+
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
37
|
+
)
|
38
|
+
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
39
|
+
(?:\s+ASC|\s+DESC)?
|
40
|
+
)
|
41
|
+
(?:\s*,\s*\g<1>)*
|
42
|
+
\z
|
43
|
+
/ix
|
44
|
+
end
|
45
|
+
|
46
|
+
def quote_column_name(name)
|
47
|
+
QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub('`', '``')}`".freeze
|
48
|
+
end
|
49
|
+
|
50
|
+
def quote_table_name(name)
|
51
|
+
QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub('`', '``').gsub(".", "`.`")}`".freeze
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
12
55
|
def cast_bound_value(value)
|
13
56
|
case value
|
14
57
|
when Rational
|
@@ -21,22 +64,11 @@ module ActiveRecord
|
|
21
64
|
"1"
|
22
65
|
when false
|
23
66
|
"0"
|
24
|
-
when ActiveSupport::Duration
|
25
|
-
warn_quote_duration_deprecated
|
26
|
-
value.to_s
|
27
67
|
else
|
28
68
|
value
|
29
69
|
end
|
30
70
|
end
|
31
71
|
|
32
|
-
def quote_column_name(name)
|
33
|
-
QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
|
34
|
-
end
|
35
|
-
|
36
|
-
def quote_table_name(name)
|
37
|
-
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
|
38
|
-
end
|
39
|
-
|
40
72
|
def unquoted_true
|
41
73
|
1
|
42
74
|
end
|
@@ -84,43 +116,6 @@ module ActiveRecord
|
|
84
116
|
super
|
85
117
|
end
|
86
118
|
end
|
87
|
-
|
88
|
-
def column_name_matcher
|
89
|
-
COLUMN_NAME
|
90
|
-
end
|
91
|
-
|
92
|
-
def column_name_with_order_matcher
|
93
|
-
COLUMN_NAME_WITH_ORDER
|
94
|
-
end
|
95
|
-
|
96
|
-
COLUMN_NAME = /
|
97
|
-
\A
|
98
|
-
(
|
99
|
-
(?:
|
100
|
-
# `table_name`.`column_name` | function(one or no argument)
|
101
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
102
|
-
)
|
103
|
-
(?:(?:\s+AS)?\s+(?:\w+|`\w+`))?
|
104
|
-
)
|
105
|
-
(?:\s*,\s*\g<1>)*
|
106
|
-
\z
|
107
|
-
/ix
|
108
|
-
|
109
|
-
COLUMN_NAME_WITH_ORDER = /
|
110
|
-
\A
|
111
|
-
(
|
112
|
-
(?:
|
113
|
-
# `table_name`.`column_name` | function(one or no argument)
|
114
|
-
((?:\w+\.|`\w+`\.)?(?:\w+|`\w+`) | \w+\((?:|\g<2>)\))
|
115
|
-
)
|
116
|
-
(?:\s+COLLATE\s+(?:\w+|"\w+"))?
|
117
|
-
(?:\s+ASC|\s+DESC)?
|
118
|
-
)
|
119
|
-
(?:\s*,\s*\g<1>)*
|
120
|
-
\z
|
121
|
-
/ix
|
122
|
-
|
123
|
-
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
124
119
|
end
|
125
120
|
end
|
126
121
|
end
|
@@ -68,6 +68,12 @@ module ActiveRecord
|
|
68
68
|
|
69
69
|
IndexDefinition.new(*index, **options)
|
70
70
|
end
|
71
|
+
rescue StatementInvalid => e
|
72
|
+
if e.message.match?(/Table '.+' doesn't exist/)
|
73
|
+
[]
|
74
|
+
else
|
75
|
+
raise
|
76
|
+
end
|
71
77
|
end
|
72
78
|
|
73
79
|
def remove_column(table_name, column_name, type = nil, **options)
|
@@ -155,7 +161,7 @@ module ActiveRecord
|
|
155
161
|
end
|
156
162
|
|
157
163
|
def valid_primary_key_options
|
158
|
-
super + [:unsigned
|
164
|
+
super + [:unsigned]
|
159
165
|
end
|
160
166
|
|
161
167
|
def create_table_definition(name, **options)
|
@@ -18,9 +18,9 @@ module ActiveRecord
|
|
18
18
|
result
|
19
19
|
end
|
20
20
|
|
21
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
|
21
|
+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) # :nodoc:
|
22
22
|
if without_prepared_statement?(binds)
|
23
|
-
execute_and_free(sql, name, async: async) do |result|
|
23
|
+
execute_and_free(sql, name, async: async, allow_retry: allow_retry) do |result|
|
24
24
|
if result
|
25
25
|
build_result(columns: result.fields, rows: result.to_a)
|
26
26
|
else
|
@@ -66,7 +66,11 @@ module ActiveRecord
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def last_inserted_id(result)
|
69
|
-
|
69
|
+
if supports_insert_returning?
|
70
|
+
super
|
71
|
+
else
|
72
|
+
@raw_connection&.last_id
|
73
|
+
end
|
70
74
|
end
|
71
75
|
|
72
76
|
def multi_statements_enabled?
|
@@ -94,12 +98,13 @@ module ActiveRecord
|
|
94
98
|
end
|
95
99
|
|
96
100
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
97
|
-
log(sql, name, async: async) do
|
101
|
+
log(sql, name, async: async) do |notification_payload|
|
98
102
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
99
103
|
sync_timezone_changes(conn)
|
100
104
|
result = conn.query(sql)
|
101
105
|
verified!
|
102
106
|
handle_warnings(sql)
|
107
|
+
notification_payload[:row_count] = result&.size || 0
|
103
108
|
result
|
104
109
|
end
|
105
110
|
end
|
@@ -113,7 +118,7 @@ module ActiveRecord
|
|
113
118
|
|
114
119
|
type_casted_binds = type_casted_binds(binds)
|
115
120
|
|
116
|
-
log(sql, name, binds, type_casted_binds, async: async) do
|
121
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
117
122
|
with_raw_connection do |conn|
|
118
123
|
sync_timezone_changes(conn)
|
119
124
|
|
@@ -139,6 +144,7 @@ module ActiveRecord
|
|
139
144
|
end
|
140
145
|
|
141
146
|
ret = yield stmt, result
|
147
|
+
notification_payload[:row_count] = result&.size || 0
|
142
148
|
result.free if result
|
143
149
|
stmt.close unless cache_stmt
|
144
150
|
ret
|