activerecord 7.1.3.4 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +507 -2133
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +4 -2
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +3 -3
  17. data/lib/active_record/associations/has_one_association.rb +2 -2
  18. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  19. data/lib/active_record/associations/join_dependency.rb +5 -7
  20. data/lib/active_record/associations/nested_error.rb +47 -0
  21. data/lib/active_record/associations/preloader/association.rb +2 -1
  22. data/lib/active_record/associations/preloader/branch.rb +7 -1
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  24. data/lib/active_record/associations/singular_association.rb +6 -0
  25. data/lib/active_record/associations/through_association.rb +1 -1
  26. data/lib/active_record/associations.rb +34 -11
  27. data/lib/active_record/attribute_assignment.rb +1 -11
  28. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  30. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  31. data/lib/active_record/attribute_methods/read.rb +1 -13
  32. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  34. data/lib/active_record/attribute_methods.rb +87 -58
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +14 -30
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +59 -38
  77. data/lib/active_record/counter_cache.rb +23 -10
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +30 -6
  84. data/lib/active_record/destroy_association_async_job.rb +1 -1
  85. data/lib/active_record/dynamic_matchers.rb +2 -2
  86. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  87. data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
  88. data/lib/active_record/encryption/encryptor.rb +17 -2
  89. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  90. data/lib/active_record/encryption/message_serializer.rb +4 -0
  91. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  93. data/lib/active_record/encryption/scheme.rb +8 -4
  94. data/lib/active_record/enum.rb +11 -2
  95. data/lib/active_record/errors.rb +16 -11
  96. data/lib/active_record/explain.rb +13 -24
  97. data/lib/active_record/fixtures.rb +37 -31
  98. data/lib/active_record/future_result.rb +17 -4
  99. data/lib/active_record/gem_version.rb +3 -3
  100. data/lib/active_record/inheritance.rb +4 -2
  101. data/lib/active_record/insert_all.rb +18 -15
  102. data/lib/active_record/integration.rb +4 -1
  103. data/lib/active_record/internal_metadata.rb +48 -34
  104. data/lib/active_record/locking/optimistic.rb +8 -7
  105. data/lib/active_record/log_subscriber.rb +0 -21
  106. data/lib/active_record/marshalling.rb +1 -1
  107. data/lib/active_record/message_pack.rb +2 -2
  108. data/lib/active_record/migration/command_recorder.rb +2 -3
  109. data/lib/active_record/migration/compatibility.rb +11 -3
  110. data/lib/active_record/migration/default_strategy.rb +4 -5
  111. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  112. data/lib/active_record/migration.rb +85 -76
  113. data/lib/active_record/model_schema.rb +34 -69
  114. data/lib/active_record/nested_attributes.rb +11 -3
  115. data/lib/active_record/normalization.rb +3 -7
  116. data/lib/active_record/persistence.rb +32 -354
  117. data/lib/active_record/query_cache.rb +18 -6
  118. data/lib/active_record/query_logs.rb +15 -0
  119. data/lib/active_record/query_logs_formatter.rb +1 -1
  120. data/lib/active_record/querying.rb +21 -9
  121. data/lib/active_record/railtie.rb +52 -64
  122. data/lib/active_record/railties/controller_runtime.rb +13 -4
  123. data/lib/active_record/railties/databases.rake +41 -44
  124. data/lib/active_record/reflection.rb +98 -37
  125. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  126. data/lib/active_record/relation/batches.rb +3 -3
  127. data/lib/active_record/relation/calculations.rb +94 -61
  128. data/lib/active_record/relation/delegation.rb +8 -11
  129. data/lib/active_record/relation/finder_methods.rb +16 -2
  130. data/lib/active_record/relation/merger.rb +4 -6
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  132. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  133. data/lib/active_record/relation/predicate_builder.rb +3 -3
  134. data/lib/active_record/relation/query_methods.rb +196 -43
  135. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  136. data/lib/active_record/relation/spawn_methods.rb +2 -18
  137. data/lib/active_record/relation/where_clause.rb +7 -19
  138. data/lib/active_record/relation.rb +500 -66
  139. data/lib/active_record/result.rb +32 -45
  140. data/lib/active_record/runtime_registry.rb +39 -0
  141. data/lib/active_record/sanitization.rb +24 -19
  142. data/lib/active_record/schema.rb +8 -6
  143. data/lib/active_record/schema_dumper.rb +19 -9
  144. data/lib/active_record/schema_migration.rb +30 -14
  145. data/lib/active_record/signed_id.rb +11 -1
  146. data/lib/active_record/statement_cache.rb +7 -7
  147. data/lib/active_record/table_metadata.rb +1 -10
  148. data/lib/active_record/tasks/database_tasks.rb +70 -42
  149. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  151. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  152. data/lib/active_record/test_fixtures.rb +82 -91
  153. data/lib/active_record/testing/query_assertions.rb +121 -0
  154. data/lib/active_record/timestamp.rb +4 -2
  155. data/lib/active_record/token_for.rb +22 -12
  156. data/lib/active_record/touch_later.rb +1 -1
  157. data/lib/active_record/transaction.rb +68 -0
  158. data/lib/active_record/transactions.rb +43 -14
  159. data/lib/active_record/translation.rb +0 -2
  160. data/lib/active_record/type/serialized.rb +1 -3
  161. data/lib/active_record/type_caster/connection.rb +4 -4
  162. data/lib/active_record/validations/associated.rb +9 -3
  163. data/lib/active_record/validations/uniqueness.rb +14 -10
  164. data/lib/active_record/validations.rb +4 -1
  165. data/lib/active_record.rb +149 -40
  166. data/lib/arel/alias_predication.rb +1 -1
  167. data/lib/arel/collectors/bind.rb +2 -0
  168. data/lib/arel/collectors/composite.rb +7 -0
  169. data/lib/arel/collectors/sql_string.rb +1 -1
  170. data/lib/arel/collectors/substitute_binds.rb +1 -1
  171. data/lib/arel/nodes/binary.rb +0 -6
  172. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  173. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  174. data/lib/arel/nodes/node.rb +4 -3
  175. data/lib/arel/nodes/sql_literal.rb +7 -0
  176. data/lib/arel/nodes.rb +2 -2
  177. data/lib/arel/predications.rb +1 -1
  178. data/lib/arel/select_manager.rb +1 -1
  179. data/lib/arel/tree_manager.rb +8 -3
  180. data/lib/arel/update_manager.rb +2 -1
  181. data/lib/arel/visitors/dot.rb +1 -0
  182. data/lib/arel/visitors/mysql.rb +9 -4
  183. data/lib/arel/visitors/postgresql.rb +1 -12
  184. data/lib/arel/visitors/to_sql.rb +31 -17
  185. data/lib/arel.rb +7 -3
  186. 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
- return unless records
194
-
195
- ite = unique_records
198
+ if records
199
+ begin
200
+ ite = unique_records
196
201
 
197
- instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
202
+ instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
198
203
 
199
- run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
200
- record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
201
- end
202
- ensure
203
- ite&.each do |i|
204
- i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
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 ActiveRecord.before_committed_on_all_records
213
- ite = unique_records
219
+ if records
220
+ if ActiveRecord.before_committed_on_all_records
221
+ ite = unique_records
214
222
 
215
- instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
216
- candidates[record] = record
217
- end
223
+ instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
224
+ candidates[record] = record
225
+ end
218
226
 
219
- run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
220
- record.before_committed! if should_run_callbacks
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
- return unless records
230
-
231
- ite = unique_records
242
+ if records
243
+ begin
244
+ ite = unique_records
232
245
 
233
- if @run_commit_callbacks
234
- instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
246
+ if @run_commit_callbacks
247
+ instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)
235
248
 
236
- run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
237
- record.committed!(should_run_callbacks: should_run_callbacks)
238
- end
239
- else
240
- while record = ite.shift
241
- # if not running callbacks, only adds the record to the parent transaction
242
- connection.add_transaction_record(record)
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
- ensure
246
- ite&.each { |i| i.committed!(should_run_callbacks: false) }
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
- ret = yield
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.connection}[rdoc-ref:ConnectionHandling#connection] returns an AbstractAdapter object, which
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 ActiveRecord adapter with a config hash, that should be the only argument"
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
- THREAD_LOCK
179
+ ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
188
180
  when Fiber
189
- FIBER_LOCK
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.new(@pool.schema_reflection, self)
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.auto_incremented_by_db?
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
- clear_cache!(new_connection: true)
719
- reset_transaction
720
- @raw_connection_dirty = false
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
- if @unconfigured_connection
777
- @lock.synchronize do
778
- if @unconfigured_connection
779
- @raw_connection = @unconfigured_connection
780
- @unconfigured_connection = nil
781
- configure_connection
782
- @verified = true
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
- reconnect!(restore_transactions: true)
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
- schema_cache.database_version
840
+ pool.server_version(self)
872
841
  end
873
842
 
874
843
  def check_version # :nodoc:
@@ -879,7 +848,7 @@ module ActiveRecord
879
848
  # numbered migration that has been executed, or 0 if no schema
880
849
  # information is present / the database is empty.
881
850
  def schema_version
882
- migration_context.current_version
851
+ pool.migration_context.current_version
883
852
  end
884
853
 
885
854
  class << self
@@ -1149,6 +1118,7 @@ module ActiveRecord
1149
1118
  statement_name: statement_name,
1150
1119
  async: async,
1151
1120
  connection: self,
1121
+ row_count: 0,
1152
1122
  &block
1153
1123
  )
1154
1124
  rescue ActiveRecord::StatementInvalid => ex
@@ -1212,7 +1182,7 @@ module ActiveRecord
1212
1182
  #
1213
1183
  # This is an internal hook to make possible connection adapters to build
1214
1184
  # custom result objects with connection-specific data.
1215
- def build_result(columns:, rows:, column_types: {})
1185
+ def build_result(columns:, rows:, column_types: nil)
1216
1186
  ActiveRecord::Result.new(columns, rows, column_types)
1217
1187
  end
1218
1188
 
@@ -1224,6 +1194,7 @@ module ActiveRecord
1224
1194
  # Implementations may assume this method will only be called while
1225
1195
  # holding @lock (or from #initialize).
1226
1196
  def configure_connection
1197
+ check_version
1227
1198
  end
1228
1199
 
1229
1200
  def default_prepared_statements
@@ -170,6 +170,10 @@ module ActiveRecord
170
170
  true
171
171
  end
172
172
 
173
+ def supports_insert_returning?
174
+ mariadb? && database_version >= "10.5.0"
175
+ end
176
+
173
177
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
174
178
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
175
179
  end
@@ -214,7 +218,7 @@ module ActiveRecord
214
218
  update("SET FOREIGN_KEY_CHECKS = 0")
215
219
  yield
216
220
  ensure
217
- update("SET FOREIGN_KEY_CHECKS = #{old}")
221
+ update("SET FOREIGN_KEY_CHECKS = #{old}") if active?
218
222
  end
219
223
  end
220
224
 
@@ -225,12 +229,12 @@ module ActiveRecord
225
229
  # Mysql2Adapter doesn't have to free a result after using it, but we use this method
226
230
  # to write stuff in an abstract way without concerning ourselves about whether it
227
231
  # needs to be explicitly freed or not.
228
- def execute_and_free(sql, name = nil, async: false) # :nodoc:
232
+ def execute_and_free(sql, name = nil, async: false, allow_retry: false) # :nodoc:
229
233
  sql = transform_query(sql)
230
234
  check_if_write_query(sql)
231
235
 
232
236
  mark_transaction_written_if_write(sql)
233
- yield raw_execute(sql, name, async: async)
237
+ yield raw_execute(sql, name, async: async, allow_retry: allow_retry)
234
238
  end
235
239
 
236
240
  def begin_db_transaction # :nodoc:
@@ -336,7 +340,7 @@ module ActiveRecord
336
340
  schema_cache.clear_data_source_cache!(table_name.to_s)
337
341
  schema_cache.clear_data_source_cache!(new_name.to_s)
338
342
  execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
339
- rename_table_indexes(table_name, new_name)
343
+ rename_table_indexes(table_name, new_name, **options)
340
344
  end
341
345
 
342
346
  # Drops a table from the database.
@@ -635,27 +639,59 @@ module ActiveRecord
635
639
  end
636
640
 
637
641
  def build_insert_sql(insert) # :nodoc:
638
- sql = +"INSERT #{insert.into} #{insert.values_list}"
639
-
640
- if insert.skip_duplicates?
641
- no_op_column = quote_column_name(insert.keys.first)
642
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
643
- elsif insert.update_duplicates?
644
- sql << " ON DUPLICATE KEY UPDATE "
645
- if insert.raw_update_sql?
646
- sql << insert.raw_update_sql
647
- else
648
- sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
649
- sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
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+)/)[1]
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