activerecord 7.1.5.1 → 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 +515 -2445
- 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 +29 -28
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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 -10
- 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/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
- 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 +53 -37
- data/lib/active_record/counter_cache.rb +18 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +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/encryption.rb +0 -2
- data/lib/active_record/enum.rb +10 -1
- 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/marshalling.rb +1 -4
- 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 -68
- data/lib/active_record/nested_attributes.rb +13 -16
- 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 +50 -62
- 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 -70
- 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 +20 -15
@@ -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
|