activerecord 7.2.2.2 → 7.2.3

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +316 -7
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/alias_tracker.rb +6 -4
  5. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  6. data/lib/active_record/associations/collection_association.rb +9 -7
  7. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  8. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  9. data/lib/active_record/attribute_methods.rb +24 -19
  10. data/lib/active_record/attributes.rb +37 -26
  11. data/lib/active_record/autosave_association.rb +22 -12
  12. data/lib/active_record/base.rb +2 -2
  13. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +49 -32
  14. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -6
  15. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +5 -1
  16. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -0
  17. data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -3
  18. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +14 -6
  19. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  20. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -3
  21. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -1
  22. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -12
  23. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  24. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1 -1
  25. data/lib/active_record/connection_adapters/postgresql_adapter.rb +8 -3
  26. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  27. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +1 -0
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +10 -5
  29. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  30. data/lib/active_record/connection_handling.rb +12 -8
  31. data/lib/active_record/core.rb +27 -7
  32. data/lib/active_record/counter_cache.rb +1 -1
  33. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  34. data/lib/active_record/delegated_type.rb +18 -18
  35. data/lib/active_record/encryption/encryptable_record.rb +1 -1
  36. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  37. data/lib/active_record/encryption/encryptor.rb +21 -20
  38. data/lib/active_record/enum.rb +13 -12
  39. data/lib/active_record/errors.rb +3 -3
  40. data/lib/active_record/fixture_set/table_row.rb +19 -2
  41. data/lib/active_record/gem_version.rb +2 -2
  42. data/lib/active_record/migration.rb +2 -1
  43. data/lib/active_record/query_logs.rb +4 -0
  44. data/lib/active_record/querying.rb +4 -4
  45. data/lib/active_record/railtie.rb +2 -2
  46. data/lib/active_record/railties/databases.rake +2 -1
  47. data/lib/active_record/relation/calculations.rb +35 -30
  48. data/lib/active_record/relation/finder_methods.rb +10 -10
  49. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
  50. data/lib/active_record/relation/query_attribute.rb +1 -1
  51. data/lib/active_record/relation/query_methods.rb +16 -9
  52. data/lib/active_record/relation/where_clause.rb +8 -2
  53. data/lib/active_record/relation.rb +15 -5
  54. data/lib/active_record/schema_dumper.rb +29 -11
  55. data/lib/active_record/secure_token.rb +3 -3
  56. data/lib/active_record/signed_id.rb +7 -6
  57. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -0
  58. data/lib/active_record/transactions.rb +3 -1
  59. data/lib/active_record.rb +1 -1
  60. data/lib/arel/collectors/bind.rb +1 -1
  61. data/lib/arel/crud.rb +2 -0
  62. data/lib/arel/delete_manager.rb +5 -0
  63. data/lib/arel/nodes/delete_statement.rb +4 -2
  64. data/lib/arel/nodes/update_statement.rb +4 -2
  65. data/lib/arel/select_manager.rb +6 -2
  66. data/lib/arel/update_manager.rb +5 -0
  67. data/lib/arel/visitors/dot.rb +2 -0
  68. data/lib/arel/visitors/to_sql.rb +3 -1
  69. metadata +8 -8
@@ -21,28 +21,34 @@ module ActiveRecord
21
21
  # your domain objects across much of Active Record, without having to
22
22
  # rely on implementation details or monkey patching.
23
23
  #
24
- # +name+ The name of the methods to define attribute methods for, and the
25
- # column which this will persist to.
24
+ # ==== Parameters
26
25
  #
27
- # +cast_type+ A symbol such as +:string+ or +:integer+, or a type object
28
- # to be used for this attribute. If this parameter is not passed, the previously
29
- # defined type (if any) will be used.
30
- # Otherwise, the type will be ActiveModel::Type::Value.
31
- # See the examples below for more information about providing custom type objects.
26
+ # [+name+]
27
+ # The name of the methods to define attribute methods for, and the
28
+ # column which this will persist to.
32
29
  #
33
- # ==== Options
30
+ # [+cast_type+]
31
+ # A symbol such as +:string+ or +:integer+, or a type object to be used
32
+ # for this attribute. If this parameter is not passed, the previously
33
+ # defined type (if any) will be used. Otherwise, the type will be
34
+ # ActiveModel::Type::Value. See the examples below for more information
35
+ # about providing custom type objects.
34
36
  #
35
- # The following options are accepted:
37
+ # ==== Options
36
38
  #
37
- # +default+ The default value to use when no value is provided. If this option
38
- # is not passed, the previously defined default value (if any) on the superclass or in the schema will be used.
39
- # Otherwise, the default will be +nil+.
39
+ # [+:default+]
40
+ # The default value to use when no value is provided. If this option is
41
+ # not passed, the previously defined default value (if any) on the
42
+ # superclass or in the schema will be used. Otherwise, the default will
43
+ # be +nil+.
40
44
  #
41
- # +array+ (PostgreSQL only) specifies that the type should be an array (see the
42
- # examples below).
45
+ # [+:array+]
46
+ # (PostgreSQL only) Specifies that the type should be an array. See the
47
+ # examples below.
43
48
  #
44
- # +range+ (PostgreSQL only) specifies that the type should be a range (see the
45
- # examples below).
49
+ # [+:range+]
50
+ # (PostgreSQL only) Specifies that the type should be a range. See the
51
+ # examples below.
46
52
  #
47
53
  # When using a symbol for +cast_type+, extra options are forwarded to the
48
54
  # constructor of the type object.
@@ -178,8 +184,8 @@ module ActiveRecord
178
184
  # @currency_converter = currency_converter
179
185
  # end
180
186
  #
181
- # # value will be the result of +deserialize+ or
182
- # # +cast+. Assumed to be an instance of +Money+ in
187
+ # # value will be the result of #deserialize or
188
+ # # #cast. Assumed to be an instance of Money in
183
189
  # # this case.
184
190
  # def serialize(value)
185
191
  # value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
@@ -217,17 +223,22 @@ module ActiveRecord
217
223
  # is provided so it can be used by plugin authors, application code
218
224
  # should probably use ClassMethods#attribute.
219
225
  #
220
- # +name+ The name of the attribute being defined. Expected to be a +String+.
226
+ # ==== Parameters
227
+ #
228
+ # [+name+]
229
+ # The name of the attribute being defined. Expected to be a +String+.
221
230
  #
222
- # +cast_type+ The type object to use for this attribute.
231
+ # [+cast_type+]
232
+ # The type object to use for this attribute.
223
233
  #
224
- # +default+ The default value to use when no value is provided. If this option
225
- # is not passed, the previous default value (if any) will be used.
226
- # Otherwise, the default will be +nil+. A proc can also be passed, and
227
- # will be called once each time a new value is needed.
234
+ # [+default+]
235
+ # The default value to use when no value is provided. If this option
236
+ # is not passed, the previous default value (if any) will be used.
237
+ # Otherwise, the default will be +nil+. A proc can also be passed, and
238
+ # will be called once each time a new value is needed.
228
239
  #
229
- # +user_provided_default+ Whether the default value should be cast using
230
- # +cast+ or +deserialize+.
240
+ # [+user_provided_default+]
241
+ # Whether the default value should be cast using +cast+ or +deserialize+.
231
242
  def define_attribute(
232
243
  name,
233
244
  cast_type,
@@ -338,19 +338,29 @@ module ActiveRecord
338
338
  return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
339
339
 
340
340
  context = validation_context if custom_validation_context?
341
+ return true if record.valid?(context)
341
342
 
342
- unless valid = record.valid?(context)
343
- if association.options[:autosave]
344
- record.errors.each { |error|
345
- self.errors.objects.append(
346
- Associations::NestedError.new(association, error)
347
- )
348
- }
349
- else
350
- errors.add(association.reflection.name)
351
- end
343
+ if context || record.changed_for_autosave?
344
+ associated_errors = record.errors.objects
345
+ else
346
+ # If there are existing invalid records in the DB, we should still be able to reference them.
347
+ # Unless a record (no matter where in the association chain) is invalid and is being changed.
348
+ associated_errors = record.errors.objects.select { |error| error.is_a?(Associations::NestedError) }
352
349
  end
353
- valid
350
+
351
+ if association.options[:autosave]
352
+ return if equal?(record)
353
+
354
+ associated_errors.each { |error|
355
+ errors.objects.append(
356
+ Associations::NestedError.new(association, error)
357
+ )
358
+ }
359
+ elsif associated_errors.any?
360
+ errors.add(association.reflection.name)
361
+ end
362
+
363
+ errors.any?
354
364
  end
355
365
 
356
366
  # Is used as an around_save callback to check while saving a collection
@@ -483,7 +493,7 @@ module ActiveRecord
483
493
 
484
494
  class_name = record._read_attribute(reflection.inverse_of.foreign_type)
485
495
 
486
- reflection.active_record != record.class.polymorphic_class_for(class_name)
496
+ reflection.active_record.polymorphic_name != class_name
487
497
  end
488
498
 
489
499
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
@@ -256,13 +256,13 @@ module ActiveRecord # :nodoc:
256
256
  # * AssociationTypeMismatch - The object assigned to the association wasn't of the type
257
257
  # specified in the association definition.
258
258
  # * AttributeAssignmentError - An error occurred while doing a mass assignment through the
259
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
259
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
260
260
  # You can inspect the +attribute+ property of the exception object to determine which attribute
261
261
  # triggered the error.
262
262
  # * ConnectionNotEstablished - No connection has been established.
263
263
  # Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
264
264
  # * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
265
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
265
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
266
266
  # The +errors+ property of this exception contains an array of
267
267
  # AttributeAssignmentError
268
268
  # objects that should be inspected to determine which attributes triggered the errors.
@@ -38,6 +38,7 @@ module ActiveRecord
38
38
 
39
39
  def schema_cache; end
40
40
  def connection_class; end
41
+ def query_cache; end
41
42
  def checkin(_); end
42
43
  def remove(_); end
43
44
  def async_executor; end
@@ -118,24 +119,30 @@ module ActiveRecord
118
119
  # * private methods that require being called in a +synchronize+ blocks
119
120
  # are now explicitly documented
120
121
  class ConnectionPool
121
- class WeakThreadKeyMap # :nodoc:
122
- # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
123
- # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
124
- def initialize
125
- @map = {}
126
- end
127
-
128
- def clear
129
- @map.clear
130
- end
122
+ # Prior to 3.3.5, WeakKeyMap had a use after free bug
123
+ # https://bugs.ruby-lang.org/issues/20688
124
+ if ObjectSpace.const_defined?(:WeakKeyMap) && RUBY_VERSION >= "3.3.5"
125
+ WeakThreadKeyMap = ObjectSpace::WeakKeyMap
126
+ else
127
+ class WeakThreadKeyMap # :nodoc:
128
+ # FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
129
+ # but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
130
+ def initialize
131
+ @map = {}
132
+ end
133
+
134
+ def clear
135
+ @map.clear
136
+ end
131
137
 
132
- def [](key)
133
- @map[key]
134
- end
138
+ def [](key)
139
+ @map[key]
140
+ end
135
141
 
136
- def []=(key, value)
137
- @map.select! { |c, _| c.alive? }
138
- @map[key] = value
142
+ def []=(key, value)
143
+ @map.select! { |c, _| c&.alive? }
144
+ @map[key] = value
145
+ end
139
146
  end
140
147
  end
141
148
 
@@ -309,8 +316,9 @@ module ActiveRecord
309
316
  # held in a cache keyed by a thread.
310
317
  def lease_connection
311
318
  lease = connection_lease
312
- lease.sticky = true
313
319
  lease.connection ||= checkout
320
+ lease.sticky = true
321
+ lease.connection
314
322
  end
315
323
 
316
324
  def permanent_lease? # :nodoc:
@@ -336,6 +344,7 @@ module ActiveRecord
336
344
  end
337
345
 
338
346
  @pinned_connection.lock_thread = ActiveSupport::IsolatedExecutionState.context if lock_thread
347
+ @pinned_connection.pinned = true
339
348
  @pinned_connection.verify! # eagerly validate the connection
340
349
  @pinned_connection.begin_transaction joinable: false, _lazy: false
341
350
  end
@@ -358,6 +367,7 @@ module ActiveRecord
358
367
  end
359
368
 
360
369
  if @pinned_connection.nil?
370
+ connection.pinned = false
361
371
  connection.steal!
362
372
  connection.lock_thread = nil
363
373
  checkin(connection)
@@ -548,20 +558,25 @@ module ActiveRecord
548
558
  # Raises:
549
559
  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
550
560
  def checkout(checkout_timeout = @checkout_timeout)
551
- if @pinned_connection
552
- @pinned_connection.lock.synchronize do
553
- synchronize do
561
+ return checkout_and_verify(acquire_connection(checkout_timeout)) unless @pinned_connection
562
+
563
+ @pinned_connection.lock.synchronize do
564
+ synchronize do
565
+ # The pinned connection may have been cleaned up before we synchronized, so check if it is still present
566
+ if @pinned_connection
554
567
  @pinned_connection.verify!
568
+
555
569
  # Any leased connection must be in @connections otherwise
556
570
  # some methods like #connected? won't behave correctly
557
571
  unless @connections.include?(@pinned_connection)
558
572
  @connections << @pinned_connection
559
573
  end
574
+
575
+ @pinned_connection
576
+ else
577
+ checkout_and_verify(acquire_connection(checkout_timeout))
560
578
  end
561
579
  end
562
- @pinned_connection
563
- else
564
- checkout_and_verify(acquire_connection(checkout_timeout))
565
580
  end
566
581
  end
567
582
 
@@ -696,6 +711,14 @@ module ActiveRecord
696
711
  Thread.pass
697
712
  end
698
713
 
714
+ def new_connection # :nodoc:
715
+ connection = db_config.new_connection
716
+ connection.pool = self
717
+ connection
718
+ rescue ConnectionNotEstablished => ex
719
+ raise ex.set_pool(self)
720
+ end
721
+
699
722
  private
700
723
  def connection_lease
701
724
  @leases[ActiveSupport::IsolatedExecutionState.context]
@@ -875,18 +898,12 @@ module ActiveRecord
875
898
  #--
876
899
  # if owner_thread param is omitted, this must be called in synchronize block
877
900
  def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
878
- @leases[owner_thread].clear(conn)
901
+ if owner_thread
902
+ @leases[owner_thread].clear(conn)
903
+ end
879
904
  end
880
905
  alias_method :release, :remove_connection_from_thread_cache
881
906
 
882
- def new_connection
883
- connection = db_config.new_connection
884
- connection.pool = self
885
- connection
886
- rescue ConnectionNotEstablished => ex
887
- raise ex.set_pool(self)
888
- end
889
-
890
907
  # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
891
908
  # to the DB is done outside main synchronized section.
892
909
  #--
@@ -191,15 +191,26 @@ module ActiveRecord
191
191
  end
192
192
  end
193
193
 
194
- attr_accessor :query_cache
195
-
196
194
  def initialize(*)
197
195
  super
198
196
  @query_cache = nil
199
197
  end
200
198
 
199
+ attr_writer :query_cache
200
+
201
+ def query_cache
202
+ if @pinned && @owner != ActiveSupport::IsolatedExecutionState.context
203
+ # With transactional tests, if the connection is pinned, any thread
204
+ # other than the one that pinned the connection need to go through the
205
+ # query cache pool, so each thread get a different cache.
206
+ pool.query_cache
207
+ else
208
+ @query_cache
209
+ end
210
+ end
211
+
201
212
  def query_cache_enabled
202
- @query_cache&.enabled?
213
+ query_cache&.enabled?
203
214
  end
204
215
 
205
216
  # Enable the query cache within the block.
@@ -238,7 +249,7 @@ module ActiveRecord
238
249
 
239
250
  # If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
240
251
  # Such queries should not be cached.
241
- if @query_cache&.enabled? && !(arel.respond_to?(:locked) && arel.locked)
252
+ if query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
242
253
  sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
243
254
 
244
255
  if async
@@ -262,7 +273,7 @@ module ActiveRecord
262
273
 
263
274
  result = nil
264
275
  @lock.synchronize do
265
- result = @query_cache[key]
276
+ result = query_cache[key]
266
277
  end
267
278
 
268
279
  if result
@@ -281,7 +292,7 @@ module ActiveRecord
281
292
  hit = true
282
293
 
283
294
  @lock.synchronize do
284
- result = @query_cache.compute_if_absent(key) do
295
+ result = query_cache.compute_if_absent(key) do
285
296
  hit = false
286
297
  yield
287
298
  end
@@ -160,6 +160,8 @@ module ActiveRecord
160
160
  end
161
161
 
162
162
  def defined_for?(to_table: nil, validate: nil, **options)
163
+ options = options.slice(*self.options.keys)
164
+
163
165
  (to_table.nil? || to_table.to_s == self.to_table) &&
164
166
  (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
165
167
  options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
@@ -186,6 +188,8 @@ module ActiveRecord
186
188
  end
187
189
 
188
190
  def defined_for?(name:, expression: nil, validate: nil, **options)
191
+ options = options.slice(*self.options.keys)
192
+
189
193
  self.name == name.to_s &&
190
194
  (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
191
195
  options.all? { |k, v| self.options[k].to_s == v.to_s }
@@ -431,7 +435,7 @@ module ActiveRecord
431
435
  #
432
436
  # == Examples
433
437
  #
434
- # # Assuming +td+ is an instance of TableDefinition
438
+ # # Assuming `td` is an instance of TableDefinition
435
439
  # td.column(:granted, :boolean, index: true)
436
440
  #
437
441
  # == Short-hand examples
@@ -186,6 +186,9 @@ module ActiveRecord
186
186
  # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
187
187
  #
188
188
  # A Symbol can be used to specify the type of the generated primary key column.
189
+ #
190
+ # A Hash can be used to specify the generated primary key column creation options.
191
+ # See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
189
192
  # [<tt>:primary_key</tt>]
190
193
  # The name of the primary key, if one is to be added automatically.
191
194
  # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
@@ -43,6 +43,7 @@ module ActiveRecord
43
43
 
44
44
  attr_reader :pool
45
45
  attr_reader :visitor, :owner, :logger, :lock
46
+ attr_accessor :pinned # :nodoc:
46
47
  alias :in_use? :owner
47
48
 
48
49
  def pool=(value)
@@ -151,6 +152,7 @@ module ActiveRecord
151
152
  end
152
153
 
153
154
  @owner = nil
155
+ @pinned = false
154
156
  @instrumenter = ActiveSupport::Notifications.instrumenter
155
157
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
156
158
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -169,6 +171,7 @@ module ActiveRecord
169
171
  @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
170
172
 
171
173
  @raw_connection_dirty = false
174
+ @last_activity = nil
172
175
  @verified = false
173
176
  end
174
177
 
@@ -218,6 +221,10 @@ module ActiveRecord
218
221
  (@config[:connection_retries] || 1).to_i
219
222
  end
220
223
 
224
+ def verify_timeout
225
+ (@config[:verify_timeout] || 2).to_i
226
+ end
227
+
221
228
  def retry_deadline
222
229
  if @config[:retry_deadline]
223
230
  @config[:retry_deadline].to_f
@@ -344,6 +351,13 @@ module ActiveRecord
344
351
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
345
352
  end
346
353
 
354
+ # Seconds since this connection last communicated with the server
355
+ def seconds_since_last_activity # :nodoc:
356
+ if @raw_connection && @last_activity
357
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
358
+ end
359
+ end
360
+
347
361
  def unprepared_statement
348
362
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
349
363
  yield
@@ -663,11 +677,12 @@ module ActiveRecord
663
677
 
664
678
  enable_lazy_transactions!
665
679
  @raw_connection_dirty = false
680
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
666
681
  @verified = true
667
682
 
668
683
  reset_transaction(restore: restore_transactions) do
669
684
  clear_cache!(new_connection: true)
670
- configure_connection
685
+ attempt_configure_connection
671
686
  end
672
687
  rescue => original_exception
673
688
  translated_exception = translate_exception_class(original_exception, nil, nil)
@@ -682,6 +697,7 @@ module ActiveRecord
682
697
  end
683
698
  end
684
699
 
700
+ @last_activity = nil
685
701
  @verified = false
686
702
 
687
703
  raise translated_exception
@@ -719,7 +735,7 @@ module ActiveRecord
719
735
  def reset!
720
736
  clear_cache!(new_connection: true)
721
737
  reset_transaction
722
- configure_connection
738
+ attempt_configure_connection
723
739
  end
724
740
 
725
741
  # Removes the connection from the pool and disconnect it.
@@ -755,7 +771,8 @@ module ActiveRecord
755
771
  if @unconfigured_connection
756
772
  @raw_connection = @unconfigured_connection
757
773
  @unconfigured_connection = nil
758
- configure_connection
774
+ attempt_configure_connection
775
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
759
776
  @verified = true
760
777
  return
761
778
  end
@@ -985,6 +1002,9 @@ module ActiveRecord
985
1002
  if @verified
986
1003
  # Cool, we're confident the connection's ready to use. (Note this might have
987
1004
  # become true during the above #materialize_transactions.)
1005
+ elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
1006
+ # We haven't actually verified the connection since we acquired it, but it
1007
+ # has been used very recently. We're going to assume it's still okay.
988
1008
  elsif reconnectable
989
1009
  if allow_retry
990
1010
  # Not sure about the connection yet, but if anything goes wrong we can
@@ -1026,6 +1046,7 @@ module ActiveRecord
1026
1046
  # Barring a known-retryable error inside the query (regardless of
1027
1047
  # whether we were in a _position_ to retry it), we should infer that
1028
1048
  # there's likely a real problem with the connection.
1049
+ @last_activity = nil
1029
1050
  @verified = false
1030
1051
  end
1031
1052
 
@@ -1040,6 +1061,7 @@ module ActiveRecord
1040
1061
  # `with_raw_connection` block only when the block is guaranteed to
1041
1062
  # exercise the raw connection.
1042
1063
  def verified!
1064
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1043
1065
  @verified = true
1044
1066
  end
1045
1067
 
@@ -1205,6 +1227,13 @@ module ActiveRecord
1205
1227
  check_version
1206
1228
  end
1207
1229
 
1230
+ def attempt_configure_connection
1231
+ configure_connection
1232
+ rescue Exception # Need to handle things such as Timeout::ExitException
1233
+ disconnect!
1234
+ raise
1235
+ end
1236
+
1208
1237
  def default_prepared_statements
1209
1238
  true
1210
1239
  end
@@ -174,6 +174,10 @@ module ActiveRecord
174
174
  mariadb? && database_version >= "10.5.0"
175
175
  end
176
176
 
177
+ def return_value_after_insert?(column) # :nodoc:
178
+ supports_insert_returning? ? column.auto_populated? : column.auto_increment?
179
+ end
180
+
177
181
  def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
178
182
  query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
179
183
  end
@@ -414,7 +418,11 @@ module ActiveRecord
414
418
  type ||= column.sql_type
415
419
 
416
420
  unless options.key?(:default)
417
- options[:default] = column.default
421
+ options[:default] = if column.default_function
422
+ -> { column.default_function }
423
+ else
424
+ column.default
425
+ end
418
426
  end
419
427
 
420
428
  unless options.key?(:null)
@@ -639,24 +647,26 @@ module ActiveRecord
639
647
  end
640
648
 
641
649
  def build_insert_sql(insert) # :nodoc:
650
+ # Can use any column as it will be assigned to itself.
642
651
  no_op_column = quote_column_name(insert.keys.first) if insert.keys.first
643
652
 
644
653
  # MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
645
654
  # then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
646
655
  if supports_insert_raw_alias_syntax?
656
+ quoted_table_name = insert.model.quoted_table_name
647
657
  values_alias = quote_table_name("#{insert.model.table_name.parameterize}_values")
648
658
  sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
649
659
 
650
660
  if insert.skip_duplicates?
651
661
  if no_op_column
652
- sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
662
+ sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{quoted_table_name}.#{no_op_column}"
653
663
  end
654
664
  elsif insert.update_duplicates?
655
665
  if insert.raw_update_sql?
656
666
  sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
657
667
  else
658
668
  sql << " ON DUPLICATE KEY UPDATE "
659
- sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
669
+ sql << insert.touch_model_timestamps_unless { |column| "#{quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
660
670
  sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
661
671
  end
662
672
  end
@@ -757,9 +767,7 @@ module ActiveRecord
757
767
 
758
768
  private
759
769
  def strip_whitespace_characters(expression)
760
- expression = expression.gsub(/\\n|\\\\/, "")
761
- expression = expression.gsub(/\s{2,}/, " ")
762
- expression
770
+ expression.gsub('\\\n', "").gsub("x0A", "").squish
763
771
  end
764
772
 
765
773
  def extended_type_map_key
@@ -110,7 +110,13 @@ module ActiveRecord
110
110
  else
111
111
  value.getlocal
112
112
  end
113
- when Date, Time
113
+ when Time
114
+ if default_timezone == :utc
115
+ value.utc? ? value : value.getutc
116
+ else
117
+ value.utc? ? value.getlocal : value
118
+ end
119
+ when Date
114
120
  value
115
121
  else
116
122
  super
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  end
24
24
  end
25
25
  else
26
- exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async) do |_, result|
26
+ exec_stmt_and_free(sql, name, binds, cache_stmt: prepare, async: async, allow_retry: allow_retry) do |_, result|
27
27
  if result
28
28
  build_result(columns: result.fields, rows: result.to_a)
29
29
  else
@@ -105,7 +105,7 @@ module ActiveRecord
105
105
  end
106
106
  end
107
107
 
108
- def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false)
108
+ def exec_stmt_and_free(sql, name, binds, cache_stmt: false, async: false, allow_retry: false)
109
109
  sql = transform_query(sql)
110
110
  check_if_write_query(sql)
111
111
 
@@ -114,7 +114,7 @@ module ActiveRecord
114
114
  type_casted_binds = type_casted_binds(binds)
115
115
 
116
116
  log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
117
- with_raw_connection do |conn|
117
+ with_raw_connection(allow_retry: allow_retry) do |conn|
118
118
  sync_timezone_changes(conn)
119
119
 
120
120
  if cache_stmt
@@ -113,7 +113,14 @@ module ActiveRecord
113
113
  end
114
114
 
115
115
  def active?
116
- connected? && @lock.synchronize { @raw_connection&.ping } || false
116
+ if connected?
117
+ @lock.synchronize do
118
+ if @raw_connection&.ping
119
+ verified!
120
+ true
121
+ end
122
+ end
123
+ end || false
117
124
  end
118
125
 
119
126
  alias :reset! :reconnect!