activerecord 7.1.4.2 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2397
  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 +27 -25
  18. data/lib/active_record/associations/join_dependency.rb +1 -1
  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 -6
  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/interval.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  58. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  59. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  60. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -11
  61. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  62. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  63. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  64. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  65. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  66. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  67. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  70. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  71. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  72. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  73. data/lib/active_record/connection_adapters.rb +121 -0
  74. data/lib/active_record/connection_handling.rb +56 -41
  75. data/lib/active_record/core.rb +52 -36
  76. data/lib/active_record/counter_cache.rb +18 -9
  77. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  78. data/lib/active_record/database_configurations/database_config.rb +15 -4
  79. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  80. data/lib/active_record/database_configurations/url_config.rb +20 -1
  81. data/lib/active_record/database_configurations.rb +1 -1
  82. data/lib/active_record/delegated_type.rb +24 -0
  83. data/lib/active_record/dynamic_matchers.rb +2 -2
  84. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  85. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  86. data/lib/active_record/encryption/encryptor.rb +17 -2
  87. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  88. data/lib/active_record/encryption/message_serializer.rb +4 -0
  89. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  90. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  91. data/lib/active_record/enum.rb +11 -2
  92. data/lib/active_record/errors.rb +16 -11
  93. data/lib/active_record/explain.rb +13 -24
  94. data/lib/active_record/fixtures.rb +37 -31
  95. data/lib/active_record/future_result.rb +8 -4
  96. data/lib/active_record/gem_version.rb +3 -3
  97. data/lib/active_record/inheritance.rb +4 -2
  98. data/lib/active_record/insert_all.rb +18 -15
  99. data/lib/active_record/integration.rb +4 -1
  100. data/lib/active_record/internal_metadata.rb +48 -34
  101. data/lib/active_record/locking/optimistic.rb +7 -6
  102. data/lib/active_record/log_subscriber.rb +0 -21
  103. data/lib/active_record/message_pack.rb +1 -1
  104. data/lib/active_record/migration/command_recorder.rb +2 -3
  105. data/lib/active_record/migration/compatibility.rb +5 -3
  106. data/lib/active_record/migration/default_strategy.rb +4 -5
  107. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  108. data/lib/active_record/migration.rb +85 -76
  109. data/lib/active_record/model_schema.rb +28 -67
  110. data/lib/active_record/nested_attributes.rb +11 -3
  111. data/lib/active_record/normalization.rb +3 -7
  112. data/lib/active_record/persistence.rb +30 -352
  113. data/lib/active_record/query_cache.rb +18 -6
  114. data/lib/active_record/query_logs.rb +15 -0
  115. data/lib/active_record/querying.rb +21 -9
  116. data/lib/active_record/railtie.rb +48 -57
  117. data/lib/active_record/railties/controller_runtime.rb +13 -4
  118. data/lib/active_record/railties/databases.rake +41 -44
  119. data/lib/active_record/reflection.rb +90 -35
  120. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  121. data/lib/active_record/relation/batches.rb +3 -3
  122. data/lib/active_record/relation/calculations.rb +94 -61
  123. data/lib/active_record/relation/delegation.rb +8 -11
  124. data/lib/active_record/relation/finder_methods.rb +16 -2
  125. data/lib/active_record/relation/merger.rb +4 -6
  126. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  127. data/lib/active_record/relation/predicate_builder.rb +3 -3
  128. data/lib/active_record/relation/query_methods.rb +196 -57
  129. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  130. data/lib/active_record/relation/spawn_methods.rb +2 -18
  131. data/lib/active_record/relation/where_clause.rb +7 -19
  132. data/lib/active_record/relation.rb +496 -72
  133. data/lib/active_record/result.rb +31 -44
  134. data/lib/active_record/runtime_registry.rb +39 -0
  135. data/lib/active_record/sanitization.rb +24 -19
  136. data/lib/active_record/schema.rb +8 -6
  137. data/lib/active_record/schema_dumper.rb +19 -9
  138. data/lib/active_record/schema_migration.rb +30 -14
  139. data/lib/active_record/signed_id.rb +11 -1
  140. data/lib/active_record/statement_cache.rb +7 -7
  141. data/lib/active_record/table_metadata.rb +1 -10
  142. data/lib/active_record/tasks/database_tasks.rb +76 -59
  143. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  146. data/lib/active_record/test_fixtures.rb +81 -91
  147. data/lib/active_record/testing/query_assertions.rb +121 -0
  148. data/lib/active_record/timestamp.rb +1 -1
  149. data/lib/active_record/token_for.rb +22 -12
  150. data/lib/active_record/touch_later.rb +1 -1
  151. data/lib/active_record/transaction.rb +68 -0
  152. data/lib/active_record/transactions.rb +43 -14
  153. data/lib/active_record/translation.rb +0 -2
  154. data/lib/active_record/type/serialized.rb +1 -3
  155. data/lib/active_record/type_caster/connection.rb +4 -4
  156. data/lib/active_record/validations/associated.rb +9 -3
  157. data/lib/active_record/validations/uniqueness.rb +14 -10
  158. data/lib/active_record/validations.rb +4 -1
  159. data/lib/active_record.rb +149 -40
  160. data/lib/arel/alias_predication.rb +1 -1
  161. data/lib/arel/collectors/bind.rb +2 -0
  162. data/lib/arel/collectors/composite.rb +7 -0
  163. data/lib/arel/collectors/sql_string.rb +1 -1
  164. data/lib/arel/collectors/substitute_binds.rb +1 -1
  165. data/lib/arel/nodes/binary.rb +0 -6
  166. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  167. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  168. data/lib/arel/nodes/node.rb +4 -3
  169. data/lib/arel/nodes/sql_literal.rb +7 -0
  170. data/lib/arel/nodes.rb +2 -2
  171. data/lib/arel/predications.rb +1 -1
  172. data/lib/arel/select_manager.rb +1 -1
  173. data/lib/arel/tree_manager.rb +3 -2
  174. data/lib/arel/update_manager.rb +2 -1
  175. data/lib/arel/visitors/dot.rb +1 -0
  176. data/lib/arel/visitors/mysql.rb +9 -4
  177. data/lib/arel/visitors/postgresql.rb +1 -12
  178. data/lib/arel/visitors/to_sql.rb +29 -16
  179. data/lib/arel.rb +7 -3
  180. 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