activerecord 8.0.0 → 8.1.4

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
@@ -5,6 +5,7 @@ require "active_record/connection_adapters/abstract/schema_dumper"
5
5
  require "active_record/connection_adapters/abstract/schema_creation"
6
6
  require "active_support/concurrency/null_lock"
7
7
  require "active_support/concurrency/load_interlock_aware_monitor"
8
+ require "active_support/concurrency/thread_monitor"
8
9
  require "arel/collectors/bind"
9
10
  require "arel/collectors/composite"
10
11
  require "arel/collectors/sql_string"
@@ -42,6 +43,8 @@ module ActiveRecord
42
43
 
43
44
  attr_reader :pool
44
45
  attr_reader :visitor, :owner, :logger, :lock
46
+ attr_reader :allow_preconnect # :nodoc:
47
+ attr_accessor :pinned # :nodoc:
45
48
  alias :in_use? :owner
46
49
 
47
50
  def pool=(value)
@@ -50,7 +53,11 @@ module ActiveRecord
50
53
  @pool = value
51
54
  end
52
55
 
53
- set_callback :checkin, :after, :enable_lazy_transactions!
56
+ def allow_preconnect=(value) # :nodoc:
57
+ @lock.synchronize do
58
+ @allow_preconnect = value
59
+ end
60
+ end
54
61
 
55
62
  def self.type_cast_config_to_integer(config)
56
63
  if config.is_a?(Integer)
@@ -119,7 +126,7 @@ module ActiveRecord
119
126
 
120
127
  # Opens a database console session.
121
128
  def self.dbconsole(config, options = {})
122
- raise NotImplementedError
129
+ raise NotImplementedError.new("#{self} should define `dbconsole` that accepts a db config and options to implement connecting to the db console")
123
130
  end
124
131
 
125
132
  def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
@@ -127,6 +134,7 @@ module ActiveRecord
127
134
 
128
135
  @raw_connection = nil
129
136
  @unconfigured_connection = nil
137
+ @connected_since = nil
130
138
 
131
139
  if config_or_deprecated_connection.is_a?(Hash)
132
140
  @config = config_or_deprecated_connection.symbolize_keys
@@ -139,6 +147,7 @@ module ActiveRecord
139
147
  # Soft-deprecated for now; we'll probably warn in future.
140
148
 
141
149
  @unconfigured_connection = config_or_deprecated_connection
150
+ @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
142
151
  @logger = deprecated_logger || ActiveRecord::Base.logger
143
152
  if deprecated_config
144
153
  @config = (deprecated_config || {}).symbolize_keys
@@ -150,9 +159,10 @@ module ActiveRecord
150
159
  end
151
160
 
152
161
  @owner = nil
153
- @instrumenter = ActiveSupport::Notifications.instrumenter
162
+ @pinned = false
154
163
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
155
164
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
165
+ @allow_preconnect = false
156
166
  @visitor = arel_visitor
157
167
  @statements = build_statement_pool
158
168
  self.lock_thread = nil
@@ -168,7 +178,10 @@ module ActiveRecord
168
178
  @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
169
179
 
170
180
  @raw_connection_dirty = false
181
+ @last_activity = nil
171
182
  @verified = false
183
+
184
+ @pool_jitter = rand * max_jitter
172
185
  end
173
186
 
174
187
  def inspect # :nodoc:
@@ -182,31 +195,23 @@ module ActiveRecord
182
195
  @lock =
183
196
  case lock_thread
184
197
  when Thread
185
- ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
198
+ ActiveSupport::Concurrency::ThreadMonitor.new
186
199
  when Fiber
187
- ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
200
+ ::Monitor.new
188
201
  else
189
202
  ActiveSupport::Concurrency::NullLock
190
203
  end
191
204
  end
192
205
 
193
- EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
194
- EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
195
- private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
196
- def with_instrumenter(instrumenter, &block) # :nodoc:
197
- Thread.handle_interrupt(EXCEPTION_NEVER) do
198
- previous_instrumenter = @instrumenter
199
- @instrumenter = instrumenter
200
- Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
201
- ensure
202
- @instrumenter = previous_instrumenter
206
+ def ensure_writes_are_allowed(sql) # :nodoc:
207
+ if preventing_writes?
208
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
203
209
  end
204
210
  end
205
211
 
206
- def check_if_write_query(sql) # :nodoc:
207
- if preventing_writes? && write_query?(sql)
208
- raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
209
- end
212
+ MAX_JITTER = 0.0..1.0 # :nodoc:
213
+ def max_jitter
214
+ (@config[:pool_jitter] || 0.2).to_f.clamp(MAX_JITTER)
210
215
  end
211
216
 
212
217
  def replica?
@@ -217,6 +222,10 @@ module ActiveRecord
217
222
  (@config[:connection_retries] || 1).to_i
218
223
  end
219
224
 
225
+ def verify_timeout
226
+ (@config[:verify_timeout] || 2).to_i
227
+ end
228
+
220
229
  def retry_deadline
221
230
  if @config[:retry_deadline]
222
231
  @config[:retry_deadline].to_f
@@ -235,9 +244,9 @@ module ActiveRecord
235
244
  # the value of +current_preventing_writes+.
236
245
  def preventing_writes?
237
246
  return true if replica?
238
- return false if connection_class.nil?
247
+ return false if connection_descriptor.nil?
239
248
 
240
- connection_class.current_preventing_writes
249
+ connection_descriptor.current_preventing_writes
241
250
  end
242
251
 
243
252
  def prepared_statements?
@@ -269,7 +278,11 @@ module ActiveRecord
269
278
  end
270
279
 
271
280
  def valid_type?(type) # :nodoc:
272
- !native_database_types[type].nil?
281
+ self.class.valid_type?(type)
282
+ end
283
+
284
+ def native_database_types # :nodoc:
285
+ self.class.native_database_types
273
286
  end
274
287
 
275
288
  # this method must only be called while holding connection pool's mutex
@@ -288,8 +301,8 @@ module ActiveRecord
288
301
  @owner = ActiveSupport::IsolatedExecutionState.context
289
302
  end
290
303
 
291
- def connection_class # :nodoc:
292
- @pool.connection_class
304
+ def connection_descriptor # :nodoc:
305
+ @pool.connection_descriptor
293
306
  end
294
307
 
295
308
  # The role (e.g. +:writing+) for the current connection. In a
@@ -308,8 +321,12 @@ module ActiveRecord
308
321
  @pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
309
322
  end
310
323
 
324
+ def pool_jitter(duration)
325
+ duration * (1.0 - @pool_jitter)
326
+ end
327
+
311
328
  # this method must only be called while holding connection pool's mutex
312
- def expire
329
+ def expire(update_idle = true) # :nodoc:
313
330
  if in_use?
314
331
  if @owner != ActiveSupport::IsolatedExecutionState.context
315
332
  raise ActiveRecordError, "Cannot expire connection, " \
@@ -317,8 +334,12 @@ module ActiveRecord
317
334
  "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
318
335
  end
319
336
 
320
- @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
321
- @owner = nil
337
+ _run_checkin_callbacks do
338
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) if update_idle
339
+ @owner = nil
340
+ enable_lazy_transactions!
341
+ unset_query_cache!
342
+ end
322
343
  else
323
344
  raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
324
345
  end
@@ -343,6 +364,28 @@ module ActiveRecord
343
364
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
344
365
  end
345
366
 
367
+ # Seconds since this connection last communicated with the server
368
+ def seconds_since_last_activity # :nodoc:
369
+ if @raw_connection && @last_activity
370
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
371
+ end
372
+ end
373
+
374
+ # Seconds since this connection was established. nil if not
375
+ # connected; infinity if the connection has been explicitly
376
+ # retired.
377
+ def connection_age # :nodoc:
378
+ if @raw_connection && @connected_since
379
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @connected_since
380
+ end
381
+ end
382
+
383
+ # Mark the connection as needing to be retired, as if the age has
384
+ # exceeded the maximum allowed.
385
+ def force_retirement # :nodoc:
386
+ @connected_since &&= -Float::INFINITY
387
+ end
388
+
346
389
  def unprepared_statement
347
390
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
348
391
  yield
@@ -557,6 +600,10 @@ module ActiveRecord
557
600
  false
558
601
  end
559
602
 
603
+ def supports_disabling_indexes?
604
+ false
605
+ end
606
+
560
607
  def return_value_after_insert?(column) # :nodoc:
561
608
  column.auto_populated?
562
609
  end
@@ -602,6 +649,11 @@ module ActiveRecord
602
649
  def drop_virtual_table(*) # :nodoc:
603
650
  end
604
651
 
652
+ # Lock used to read an existing record after a duplicate INSERT in a transaction.
653
+ def create_or_find_by_lock # :nodoc:
654
+ true
655
+ end
656
+
605
657
  def advisory_locks_enabled? # :nodoc:
606
658
  supports_advisory_locks? && @advisory_locks_enabled
607
659
  end
@@ -666,32 +718,36 @@ module ActiveRecord
666
718
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
667
719
 
668
720
  @lock.synchronize do
669
- reconnect
721
+ attempt_configure_connection do
722
+ @allow_preconnect = false
670
723
 
671
- enable_lazy_transactions!
672
- @raw_connection_dirty = false
673
- @verified = true
674
-
675
- reset_transaction(restore: restore_transactions) do
676
- clear_cache!(new_connection: true)
677
- configure_connection
678
- end
679
- rescue => original_exception
680
- translated_exception = translate_exception_class(original_exception, nil, nil)
681
- retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
724
+ reconnect
682
725
 
683
- if !retry_deadline_exceeded && retries_available > 0
684
- retries_available -= 1
726
+ enable_lazy_transactions!
727
+ @raw_connection_dirty = false
728
+ @last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
729
+ @verified = true
730
+ @allow_preconnect = true
685
731
 
686
- if retryable_connection_error?(translated_exception)
687
- backoff(connection_retries - retries_available)
688
- retry
732
+ reset_transaction(restore: restore_transactions) do
733
+ clear_cache!(new_connection: true)
734
+ configure_connection
689
735
  end
690
- end
736
+ rescue => original_exception
737
+ translated_exception = translate_exception_class(original_exception, nil, nil)
738
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
691
739
 
692
- @verified = false
740
+ if !retry_deadline_exceeded && retries_available > 0
741
+ retries_available -= 1
742
+
743
+ if retryable_connection_error?(translated_exception)
744
+ backoff(connection_retries - retries_available)
745
+ retry
746
+ end
747
+ end
693
748
 
694
- raise translated_exception
749
+ raise translated_exception
750
+ end
695
751
  end
696
752
  end
697
753
 
@@ -702,6 +758,9 @@ module ActiveRecord
702
758
  clear_cache!(new_connection: true)
703
759
  reset_transaction
704
760
  @raw_connection_dirty = false
761
+ @connected_since = nil
762
+ @last_activity = nil
763
+ @verified = false
705
764
  end
706
765
  end
707
766
 
@@ -724,9 +783,11 @@ module ActiveRecord
724
783
  # should call super immediately after resetting the connection (and while
725
784
  # still holding @lock).
726
785
  def reset!
727
- clear_cache!(new_connection: true)
728
- reset_transaction
729
- configure_connection
786
+ attempt_configure_connection do
787
+ clear_cache!(new_connection: true)
788
+ reset_transaction
789
+ configure_connection
790
+ end
730
791
  end
731
792
 
732
793
  # Removes the connection from the pool and disconnect it.
@@ -760,10 +821,14 @@ module ActiveRecord
760
821
  unless active?
761
822
  @lock.synchronize do
762
823
  if @unconfigured_connection
763
- @raw_connection = @unconfigured_connection
764
- @unconfigured_connection = nil
765
- configure_connection
766
- @verified = true
824
+ attempt_configure_connection do
825
+ @raw_connection = @unconfigured_connection
826
+ @unconfigured_connection = nil
827
+ configure_connection
828
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
829
+ @verified = true
830
+ @allow_preconnect = true
831
+ end
767
832
  return
768
833
  end
769
834
 
@@ -771,6 +836,7 @@ module ActiveRecord
771
836
  end
772
837
  end
773
838
 
839
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
774
840
  @verified = true
775
841
  end
776
842
 
@@ -780,8 +846,15 @@ module ActiveRecord
780
846
  end
781
847
 
782
848
  def clean! # :nodoc:
783
- @raw_connection_dirty = false
784
- @verified = nil
849
+ _run_checkout_callbacks do
850
+ @raw_connection_dirty = false
851
+ @verified = nil
852
+ end
853
+ self
854
+ end
855
+
856
+ def verified? # :nodoc:
857
+ @verified
785
858
  end
786
859
 
787
860
  # Provides access to the underlying database driver for this adapter. For
@@ -869,7 +942,7 @@ module ActiveRecord
869
942
  def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
870
943
  mapping.register_type(key) do |*args|
871
944
  precision = extract_precision(args.last)
872
- klass.new(precision: precision, **kwargs)
945
+ klass.new(precision: precision, **kwargs).freeze
873
946
  end
874
947
  end
875
948
 
@@ -881,6 +954,10 @@ module ActiveRecord
881
954
  end
882
955
  end
883
956
 
957
+ def valid_type?(type) # :nodoc:
958
+ !native_database_types[type].nil?
959
+ end
960
+
884
961
  private
885
962
  def initialize_type_map(m)
886
963
  register_class_with_limit m, %r(boolean)i, Type::Boolean
@@ -900,7 +977,7 @@ module ActiveRecord
900
977
  m.alias_type %r(number)i, "decimal"
901
978
  m.alias_type %r(double)i, "float"
902
979
 
903
- m.register_type %r(^json)i, Type::Json.new
980
+ m.register_type %r(^json)i, Type::Json.new.freeze
904
981
 
905
982
  m.register_type(%r(decimal)i) do |sql_type|
906
983
  scale = extract_scale(sql_type)
@@ -908,9 +985,9 @@ module ActiveRecord
908
985
 
909
986
  if scale == 0
910
987
  # FIXME: Remove this class as well
911
- Type::DecimalWithoutScale.new(precision: precision)
988
+ Type::DecimalWithoutScale.new(precision: precision).freeze
912
989
  else
913
- Type::Decimal.new(precision: precision, scale: scale)
990
+ Type::Decimal.new(precision: precision, scale: scale).freeze
914
991
  end
915
992
  end
916
993
  end
@@ -918,7 +995,7 @@ module ActiveRecord
918
995
  def register_class_with_limit(mapping, key, klass)
919
996
  mapping.register_type(key) do |*args|
920
997
  limit = extract_limit(args.last)
921
- klass.new(limit: limit)
998
+ klass.new(limit: limit).freeze
922
999
  end
923
1000
  end
924
1001
 
@@ -992,6 +1069,9 @@ module ActiveRecord
992
1069
  if @verified
993
1070
  # Cool, we're confident the connection's ready to use. (Note this might have
994
1071
  # become true during the above #materialize_transactions.)
1072
+ elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
1073
+ # We haven't actually verified the connection since we acquired it, but it
1074
+ # has been used very recently. We're going to assume it's still okay.
995
1075
  elsif reconnectable
996
1076
  if allow_retry
997
1077
  # Not sure about the connection yet, but if anything goes wrong we can
@@ -1033,10 +1113,18 @@ module ActiveRecord
1033
1113
  # Barring a known-retryable error inside the query (regardless of
1034
1114
  # whether we were in a _position_ to retry it), we should infer that
1035
1115
  # there's likely a real problem with the connection.
1116
+ @last_activity = nil
1036
1117
  @verified = false
1037
1118
  end
1038
1119
 
1039
1120
  raise translated_exception
1121
+ rescue Exception
1122
+ # A non-StandardError (a Timeout, or a fiber scheduler's cancel) abandoned
1123
+ # the query partway through, so we mark the connection unverified, just as
1124
+ # a failed query would, forcing a reconnect before it's used again.
1125
+ @last_activity = nil
1126
+ @verified = false
1127
+ raise
1040
1128
  ensure
1041
1129
  dirty_current_transaction if materialize_transactions
1042
1130
  end
@@ -1047,6 +1135,7 @@ module ActiveRecord
1047
1135
  # `with_raw_connection` block only when the block is guaranteed to
1048
1136
  # exercise the raw connection.
1049
1137
  def verified!
1138
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1050
1139
  @verified = true
1051
1140
  end
1052
1141
 
@@ -1074,7 +1163,7 @@ module ActiveRecord
1074
1163
  end
1075
1164
 
1076
1165
  def reconnect
1077
- raise NotImplementedError
1166
+ raise NotImplementedError.new("#{self.class} should define `reconnect` to implement adapter-specific logic for reconnecting to the database")
1078
1167
  end
1079
1168
 
1080
1169
  # Returns a raw connection for internal use with methods that are known
@@ -1125,16 +1214,18 @@ module ActiveRecord
1125
1214
  active_record_error
1126
1215
  end
1127
1216
 
1128
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1129
- @instrumenter.instrument(
1217
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, allow_retry: false, &block) # :doc:
1218
+ instrumenter.instrument(
1130
1219
  "sql.active_record",
1131
1220
  sql: sql,
1132
1221
  name: name,
1133
1222
  binds: binds,
1134
1223
  type_casted_binds: type_casted_binds,
1135
1224
  async: async,
1225
+ allow_retry: allow_retry,
1136
1226
  connection: self,
1137
1227
  transaction: current_transaction.user_transaction.presence,
1228
+ affected_rows: 0,
1138
1229
  row_count: 0,
1139
1230
  &block
1140
1231
  )
@@ -1142,6 +1233,10 @@ module ActiveRecord
1142
1233
  raise ex.set_query(sql, binds)
1143
1234
  end
1144
1235
 
1236
+ def instrumenter # :nodoc:
1237
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
1238
+ end
1239
+
1145
1240
  def translate_exception(exception, message:, sql:, binds:)
1146
1241
  # override in derived class
1147
1242
  case exception
@@ -1203,6 +1298,13 @@ module ActiveRecord
1203
1298
  check_version
1204
1299
  end
1205
1300
 
1301
+ def attempt_configure_connection
1302
+ yield
1303
+ rescue Exception # Need to handle things such as Timeout::ExitException
1304
+ disconnect!
1305
+ raise
1306
+ end
1307
+
1206
1308
  def default_prepared_statements
1207
1309
  true
1208
1310
  end