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.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2445
  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 +14 -7
  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 +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  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 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  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 +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  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 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -23
  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 +1 -1
  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 +15 -13
  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 +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  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 +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  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 +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. 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
- 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.
@@ -867,7 +837,7 @@ module ActiveRecord
867
837
  end
868
838
 
869
839
  def database_version # :nodoc:
870
- schema_cache.database_version
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+)/)[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
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, :auto_increment]
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
- @raw_connection&.last_id
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