activerecord 7.2.2.1 → 8.0.5

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -677
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +34 -10
  7. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  8. data/lib/active_record/associations/builder/association.rb +7 -6
  9. data/lib/active_record/associations/collection_association.rb +10 -8
  10. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  11. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  12. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  13. data/lib/active_record/associations/join_dependency.rb +2 -2
  14. data/lib/active_record/associations/preloader/association.rb +2 -2
  15. data/lib/active_record/associations/preloader/batch.rb +7 -1
  16. data/lib/active_record/associations/singular_association.rb +8 -3
  17. data/lib/active_record/associations.rb +34 -4
  18. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  19. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  20. data/lib/active_record/attribute_methods/query.rb +34 -0
  21. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  22. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  23. data/lib/active_record/attribute_methods.rb +24 -19
  24. data/lib/active_record/attributes.rb +37 -26
  25. data/lib/active_record/autosave_association.rb +91 -39
  26. data/lib/active_record/base.rb +2 -2
  27. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  28. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  29. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  30. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  34. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -10
  37. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  38. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
  39. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
  40. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
  41. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  42. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  43. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  44. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
  45. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  46. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  47. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  48. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  49. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  50. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  51. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  52. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -24
  53. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  54. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
  55. data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
  56. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  57. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  58. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  59. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  60. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  61. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  62. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +10 -1
  63. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
  64. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  65. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  66. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  67. data/lib/active_record/connection_adapters.rb +0 -56
  68. data/lib/active_record/connection_handling.rb +35 -9
  69. data/lib/active_record/core.rb +57 -22
  70. data/lib/active_record/counter_cache.rb +1 -1
  71. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  72. data/lib/active_record/database_configurations/database_config.rb +4 -0
  73. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  74. data/lib/active_record/delegated_type.rb +18 -18
  75. data/lib/active_record/encryption/config.rb +3 -1
  76. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  77. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  78. data/lib/active_record/encryption/encryptor.rb +37 -28
  79. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  80. data/lib/active_record/encryption/scheme.rb +8 -1
  81. data/lib/active_record/enum.rb +22 -34
  82. data/lib/active_record/errors.rb +16 -8
  83. data/lib/active_record/fixture_set/table_row.rb +19 -2
  84. data/lib/active_record/fixtures.rb +0 -2
  85. data/lib/active_record/future_result.rb +15 -9
  86. data/lib/active_record/gem_version.rb +4 -4
  87. data/lib/active_record/insert_all.rb +3 -3
  88. data/lib/active_record/locking/optimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +5 -11
  90. data/lib/active_record/migration/command_recorder.rb +32 -12
  91. data/lib/active_record/migration/compatibility.rb +5 -2
  92. data/lib/active_record/migration.rb +40 -43
  93. data/lib/active_record/model_schema.rb +3 -4
  94. data/lib/active_record/nested_attributes.rb +4 -6
  95. data/lib/active_record/persistence.rb +128 -130
  96. data/lib/active_record/query_logs.rb +106 -50
  97. data/lib/active_record/query_logs_formatter.rb +17 -28
  98. data/lib/active_record/querying.rb +12 -12
  99. data/lib/active_record/railtie.rb +4 -28
  100. data/lib/active_record/railties/databases.rake +11 -34
  101. data/lib/active_record/reflection.rb +18 -21
  102. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  103. data/lib/active_record/relation/batches.rb +132 -72
  104. data/lib/active_record/relation/calculations.rb +71 -65
  105. data/lib/active_record/relation/delegation.rb +25 -14
  106. data/lib/active_record/relation/finder_methods.rb +32 -31
  107. data/lib/active_record/relation/merger.rb +8 -8
  108. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  109. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
  110. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  111. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  112. data/lib/active_record/relation/predicate_builder.rb +13 -0
  113. data/lib/active_record/relation/query_attribute.rb +1 -1
  114. data/lib/active_record/relation/query_methods.rb +121 -70
  115. data/lib/active_record/relation/spawn_methods.rb +7 -7
  116. data/lib/active_record/relation/where_clause.rb +9 -3
  117. data/lib/active_record/relation.rb +95 -67
  118. data/lib/active_record/result.rb +66 -4
  119. data/lib/active_record/sanitization.rb +7 -6
  120. data/lib/active_record/schema_dumper.rb +34 -11
  121. data/lib/active_record/schema_migration.rb +2 -1
  122. data/lib/active_record/scoping/named.rb +5 -2
  123. data/lib/active_record/secure_token.rb +3 -3
  124. data/lib/active_record/signed_id.rb +7 -6
  125. data/lib/active_record/statement_cache.rb +14 -14
  126. data/lib/active_record/store.rb +7 -3
  127. data/lib/active_record/table_metadata.rb +1 -3
  128. data/lib/active_record/tasks/database_tasks.rb +69 -60
  129. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  130. data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
  131. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  132. data/lib/active_record/test_databases.rb +1 -1
  133. data/lib/active_record/test_fixtures.rb +12 -0
  134. data/lib/active_record/token_for.rb +1 -1
  135. data/lib/active_record/transactions.rb +8 -7
  136. data/lib/active_record/type/serialized.rb +5 -0
  137. data/lib/active_record/validations/uniqueness.rb +8 -8
  138. data/lib/active_record.rb +22 -49
  139. data/lib/arel/collectors/bind.rb +2 -2
  140. data/lib/arel/collectors/sql_string.rb +1 -1
  141. data/lib/arel/collectors/substitute_binds.rb +2 -2
  142. data/lib/arel/crud.rb +2 -0
  143. data/lib/arel/delete_manager.rb +5 -0
  144. data/lib/arel/nodes/binary.rb +1 -1
  145. data/lib/arel/nodes/delete_statement.rb +4 -2
  146. data/lib/arel/nodes/node.rb +1 -1
  147. data/lib/arel/nodes/sql_literal.rb +1 -1
  148. data/lib/arel/nodes/update_statement.rb +4 -2
  149. data/lib/arel/predications.rb +1 -3
  150. data/lib/arel/select_manager.rb +6 -2
  151. data/lib/arel/table.rb +3 -7
  152. data/lib/arel/update_manager.rb +5 -0
  153. data/lib/arel/visitors/dot.rb +2 -0
  154. data/lib/arel/visitors/to_sql.rb +3 -1
  155. metadata +11 -15
  156. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -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.
@@ -345,6 +348,15 @@ module ActiveRecord
345
348
  # # Creates a table called 'assemblies_parts' with no id.
346
349
  # create_join_table(:assemblies, :parts)
347
350
  #
351
+ # # Creates a table called 'paper_boxes_papers' with no id.
352
+ # create_join_table('papers', 'paper_boxes')
353
+ #
354
+ # A duplicate prefix is combined into a single prefix. This is useful for
355
+ # namespaced models like Music::Artist and Music::Record:
356
+ #
357
+ # # Creates a table called 'music_artists_records' with no id.
358
+ # create_join_table('music_artists', 'music_records')
359
+ #
348
360
  # You can pass an +options+ hash which can include the following keys:
349
361
  # [<tt>:table_name</tt>]
350
362
  # Sets the table name, overriding the default.
@@ -516,7 +528,7 @@ module ActiveRecord
516
528
  raise NotImplementedError, "rename_table is not implemented"
517
529
  end
518
530
 
519
- # Drops a table from the database.
531
+ # Drops a table or tables from the database.
520
532
  #
521
533
  # [<tt>:force</tt>]
522
534
  # Set to +:cascade+ to drop dependent objects as well.
@@ -527,17 +539,19 @@ module ActiveRecord
527
539
  #
528
540
  # Although this command ignores most +options+ and the block if one is given,
529
541
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
530
- # In that case, +options+ and the block will be used by #create_table.
531
- def drop_table(table_name, **options)
532
- schema_cache.clear_data_source_cache!(table_name.to_s)
533
- execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
542
+ # In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
543
+ def drop_table(*table_names, **options)
544
+ table_names.each do |table_name|
545
+ schema_cache.clear_data_source_cache!(table_name.to_s)
546
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
547
+ end
534
548
  end
535
549
 
536
550
  # Add a new +type+ column named +column_name+ to +table_name+.
537
551
  #
538
552
  # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
539
553
  #
540
- # The +type+ parameter is normally one of the migrations native types,
554
+ # The +type+ parameter is normally one of the migration's native types,
541
555
  # which is one of the following:
542
556
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
543
557
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
@@ -844,6 +858,16 @@ module ActiveRecord
844
858
  #
845
859
  # Note: only supported by PostgreSQL.
846
860
  #
861
+ # ====== Creating an index where NULLs are treated equally
862
+ #
863
+ # add_index(:people, :last_name, nulls_not_distinct: true)
864
+ #
865
+ # generates:
866
+ #
867
+ # CREATE INDEX index_people_on_last_name ON people (last_name) NULLS NOT DISTINCT
868
+ #
869
+ # Note: only supported by PostgreSQL version 15.0.0 and greater.
870
+ #
847
871
  # ====== Creating an index with a specific method
848
872
  #
849
873
  # add_index(:developers, :name, using: 'btree')
@@ -1313,7 +1337,6 @@ module ActiveRecord
1313
1337
  execute schema_creation.accept(at)
1314
1338
  end
1315
1339
 
1316
-
1317
1340
  # Checks to see if a check constraint exists on a table for a given check constraint definition.
1318
1341
  #
1319
1342
  # check_constraint_exists?(:products, name: "price_check")
@@ -1325,6 +1348,13 @@ module ActiveRecord
1325
1348
  check_constraint_for(table_name, **options).present?
1326
1349
  end
1327
1350
 
1351
+ def remove_constraint(table_name, constraint_name) # :nodoc:
1352
+ at = create_alter_table(table_name)
1353
+ at.drop_constraint(constraint_name)
1354
+
1355
+ execute schema_creation.accept(at)
1356
+ end
1357
+
1328
1358
  def dump_schema_information # :nodoc:
1329
1359
  versions = pool.schema_migration.versions
1330
1360
  insert_versions_sql(versions) if versions.any?
@@ -1530,11 +1560,11 @@ module ActiveRecord
1530
1560
  non_combinable_operations = []
1531
1561
 
1532
1562
  operations.each do |command, args|
1533
- table, arguments = args.shift, args
1563
+ args.shift # remove table_name
1534
1564
  method = :"#{command}_for_alter"
1535
1565
 
1536
1566
  if respond_to?(method, true)
1537
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1567
+ sqls, procs = Array(send(method, table_name, *args)).partition { |v| v.is_a?(String) }
1538
1568
  sql_fragments.concat(sqls)
1539
1569
  non_combinable_operations.concat(procs)
1540
1570
  else
@@ -1542,7 +1572,7 @@ module ActiveRecord
1542
1572
  non_combinable_operations.each(&:call)
1543
1573
  sql_fragments = []
1544
1574
  non_combinable_operations = []
1545
- send(command, table, *arguments)
1575
+ send(command, table_name, *args)
1546
1576
  end
1547
1577
  end
1548
1578
 
@@ -448,10 +448,14 @@ module ActiveRecord
448
448
  # = Active Record Real \Transaction
449
449
  class RealTransaction < Transaction
450
450
  def materialize!
451
- if isolation_level
452
- connection.begin_isolated_db_transaction(isolation_level)
451
+ if joinable?
452
+ if isolation_level
453
+ connection.begin_isolated_db_transaction(isolation_level)
454
+ else
455
+ connection.begin_db_transaction
456
+ end
453
457
  else
454
- connection.begin_db_transaction
458
+ connection.begin_deferred_transaction(isolation_level)
455
459
  end
456
460
 
457
461
  super
@@ -472,13 +476,19 @@ module ActiveRecord
472
476
  end
473
477
 
474
478
  def rollback
475
- connection.rollback_db_transaction if materialized?
479
+ if materialized?
480
+ connection.rollback_db_transaction
481
+ connection.reset_isolation_level if isolation_level
482
+ end
476
483
  @state.full_rollback!
477
484
  @instrumenter.finish(:rollback) if materialized?
478
485
  end
479
486
 
480
487
  def commit
481
- connection.commit_db_transaction if materialized?
488
+ if materialized?
489
+ connection.commit_db_transaction
490
+ connection.reset_isolation_level if isolation_level
491
+ end
482
492
  @state.full_commit!
483
493
  @instrumenter.finish(:commit) if materialized?
484
494
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "active_record/connection_adapters/sql_type_metadata"
5
4
  require "active_record/connection_adapters/abstract/schema_dumper"
6
5
  require "active_record/connection_adapters/abstract/schema_creation"
@@ -43,6 +42,7 @@ module ActiveRecord
43
42
 
44
43
  attr_reader :pool
45
44
  attr_reader :visitor, :owner, :logger, :lock
45
+ attr_accessor :pinned # :nodoc:
46
46
  alias :in_use? :owner
47
47
 
48
48
  def pool=(value)
@@ -151,7 +151,7 @@ module ActiveRecord
151
151
  end
152
152
 
153
153
  @owner = nil
154
- @instrumenter = ActiveSupport::Notifications.instrumenter
154
+ @pinned = false
155
155
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
156
156
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
157
157
  @visitor = arel_visitor
@@ -169,6 +169,7 @@ module ActiveRecord
169
169
  @default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
170
170
 
171
171
  @raw_connection_dirty = false
172
+ @last_activity = nil
172
173
  @verified = false
173
174
  end
174
175
 
@@ -191,19 +192,6 @@ module ActiveRecord
191
192
  end
192
193
  end
193
194
 
194
- EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
195
- EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
196
- private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
197
- def with_instrumenter(instrumenter, &block) # :nodoc:
198
- Thread.handle_interrupt(EXCEPTION_NEVER) do
199
- previous_instrumenter = @instrumenter
200
- @instrumenter = instrumenter
201
- Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
202
- ensure
203
- @instrumenter = previous_instrumenter
204
- end
205
- end
206
-
207
195
  def check_if_write_query(sql) # :nodoc:
208
196
  if preventing_writes? && write_query?(sql)
209
197
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
@@ -218,6 +206,10 @@ module ActiveRecord
218
206
  (@config[:connection_retries] || 1).to_i
219
207
  end
220
208
 
209
+ def verify_timeout
210
+ (@config[:verify_timeout] || 2).to_i
211
+ end
212
+
221
213
  def retry_deadline
222
214
  if @config[:retry_deadline]
223
215
  @config[:retry_deadline].to_f
@@ -236,9 +228,9 @@ module ActiveRecord
236
228
  # the value of +current_preventing_writes+.
237
229
  def preventing_writes?
238
230
  return true if replica?
239
- return false if connection_class.nil?
231
+ return false if connection_descriptor.nil?
240
232
 
241
- connection_class.current_preventing_writes
233
+ connection_descriptor.current_preventing_writes
242
234
  end
243
235
 
244
236
  def prepared_statements?
@@ -289,8 +281,8 @@ module ActiveRecord
289
281
  @owner = ActiveSupport::IsolatedExecutionState.context
290
282
  end
291
283
 
292
- def connection_class # :nodoc:
293
- @pool.connection_class
284
+ def connection_descriptor # :nodoc:
285
+ @pool.connection_descriptor
294
286
  end
295
287
 
296
288
  # The role (e.g. +:writing+) for the current connection. In a
@@ -344,6 +336,13 @@ module ActiveRecord
344
336
  Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
345
337
  end
346
338
 
339
+ # Seconds since this connection last communicated with the server
340
+ def seconds_since_last_activity # :nodoc:
341
+ if @raw_connection && @last_activity
342
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
343
+ end
344
+ end
345
+
347
346
  def unprepared_statement
348
347
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
349
348
  yield
@@ -576,23 +575,31 @@ module ActiveRecord
576
575
  end
577
576
 
578
577
  # This is meant to be implemented by the adapters that support custom enum types
579
- def create_enum(*) # :nodoc:
578
+ def create_enum(...) # :nodoc:
580
579
  end
581
580
 
582
581
  # This is meant to be implemented by the adapters that support custom enum types
583
- def drop_enum(*) # :nodoc:
582
+ def drop_enum(...) # :nodoc:
584
583
  end
585
584
 
586
585
  # This is meant to be implemented by the adapters that support custom enum types
587
- def rename_enum(*) # :nodoc:
586
+ def rename_enum(...) # :nodoc:
588
587
  end
589
588
 
590
589
  # This is meant to be implemented by the adapters that support custom enum types
591
- def add_enum_value(*) # :nodoc:
590
+ def add_enum_value(...) # :nodoc:
592
591
  end
593
592
 
594
593
  # This is meant to be implemented by the adapters that support custom enum types
595
- def rename_enum_value(*) # :nodoc:
594
+ def rename_enum_value(...) # :nodoc:
595
+ end
596
+
597
+ # This is meant to be implemented by the adapters that support virtual tables
598
+ def create_virtual_table(*) # :nodoc:
599
+ end
600
+
601
+ # This is meant to be implemented by the adapters that support virtual tables
602
+ def drop_virtual_table(*) # :nodoc:
596
603
  end
597
604
 
598
605
  def advisory_locks_enabled? # :nodoc:
@@ -659,32 +666,33 @@ module ActiveRecord
659
666
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
660
667
 
661
668
  @lock.synchronize do
662
- reconnect
669
+ attempt_configure_connection do
670
+ reconnect
663
671
 
664
- enable_lazy_transactions!
665
- @raw_connection_dirty = false
666
- @verified = true
672
+ enable_lazy_transactions!
673
+ @raw_connection_dirty = false
674
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
675
+ @verified = true
667
676
 
668
- reset_transaction(restore: restore_transactions) do
669
- clear_cache!(new_connection: true)
670
- configure_connection
671
- end
672
- rescue => original_exception
673
- translated_exception = translate_exception_class(original_exception, nil, nil)
674
- retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
677
+ reset_transaction(restore: restore_transactions) do
678
+ clear_cache!(new_connection: true)
679
+ configure_connection
680
+ end
681
+ rescue => original_exception
682
+ translated_exception = translate_exception_class(original_exception, nil, nil)
683
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
675
684
 
676
- if !retry_deadline_exceeded && retries_available > 0
677
- retries_available -= 1
685
+ if !retry_deadline_exceeded && retries_available > 0
686
+ retries_available -= 1
678
687
 
679
- if retryable_connection_error?(translated_exception)
680
- backoff(connection_retries - retries_available)
681
- retry
688
+ if retryable_connection_error?(translated_exception)
689
+ backoff(connection_retries - retries_available)
690
+ retry
691
+ end
682
692
  end
683
- end
684
693
 
685
- @verified = false
686
-
687
- raise translated_exception
694
+ raise translated_exception
695
+ end
688
696
  end
689
697
  end
690
698
 
@@ -692,6 +700,8 @@ module ActiveRecord
692
700
  # method does nothing.
693
701
  def disconnect!
694
702
  @lock.synchronize do
703
+ @last_activity = nil
704
+ @verified = false
695
705
  clear_cache!(new_connection: true)
696
706
  reset_transaction
697
707
  @raw_connection_dirty = false
@@ -717,9 +727,11 @@ module ActiveRecord
717
727
  # should call super immediately after resetting the connection (and while
718
728
  # still holding @lock).
719
729
  def reset!
720
- clear_cache!(new_connection: true)
721
- reset_transaction
722
- configure_connection
730
+ attempt_configure_connection do
731
+ clear_cache!(new_connection: true)
732
+ reset_transaction
733
+ configure_connection
734
+ end
723
735
  end
724
736
 
725
737
  # Removes the connection from the pool and disconnect it.
@@ -753,10 +765,13 @@ module ActiveRecord
753
765
  unless active?
754
766
  @lock.synchronize do
755
767
  if @unconfigured_connection
756
- @raw_connection = @unconfigured_connection
757
- @unconfigured_connection = nil
758
- configure_connection
759
- @verified = true
768
+ attempt_configure_connection do
769
+ @raw_connection = @unconfigured_connection
770
+ @unconfigured_connection = nil
771
+ configure_connection
772
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
773
+ @verified = true
774
+ end
760
775
  return
761
776
  end
762
777
 
@@ -985,6 +1000,9 @@ module ActiveRecord
985
1000
  if @verified
986
1001
  # Cool, we're confident the connection's ready to use. (Note this might have
987
1002
  # become true during the above #materialize_transactions.)
1003
+ elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
1004
+ # We haven't actually verified the connection since we acquired it, but it
1005
+ # has been used very recently. We're going to assume it's still okay.
988
1006
  elsif reconnectable
989
1007
  if allow_retry
990
1008
  # Not sure about the connection yet, but if anything goes wrong we can
@@ -1026,6 +1044,7 @@ module ActiveRecord
1026
1044
  # Barring a known-retryable error inside the query (regardless of
1027
1045
  # whether we were in a _position_ to retry it), we should infer that
1028
1046
  # there's likely a real problem with the connection.
1047
+ @last_activity = nil
1029
1048
  @verified = false
1030
1049
  end
1031
1050
 
@@ -1040,11 +1059,13 @@ module ActiveRecord
1040
1059
  # `with_raw_connection` block only when the block is guaranteed to
1041
1060
  # exercise the raw connection.
1042
1061
  def verified!
1062
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1043
1063
  @verified = true
1044
1064
  end
1045
1065
 
1046
1066
  def retryable_connection_error?(exception)
1047
- exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
1067
+ (exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
1068
+ exception.is_a?(ConnectionFailed)
1048
1069
  end
1049
1070
 
1050
1071
  def invalidate_transaction(exception)
@@ -1105,24 +1126,25 @@ module ActiveRecord
1105
1126
  end
1106
1127
  end
1107
1128
 
1108
- def translate_exception_class(e, sql, binds)
1109
- message = "#{e.class.name}: #{e.message}"
1129
+ def translate_exception_class(native_error, sql, binds)
1130
+ return native_error if native_error.is_a?(ActiveRecordError)
1110
1131
 
1111
- exception = translate_exception(
1112
- e, message: message, sql: sql, binds: binds
1132
+ message = "#{native_error.class.name}: #{native_error.message}"
1133
+
1134
+ active_record_error = translate_exception(
1135
+ native_error, message: message, sql: sql, binds: binds
1113
1136
  )
1114
- exception.set_backtrace e.backtrace
1115
- exception
1137
+ active_record_error.set_backtrace(native_error.backtrace)
1138
+ active_record_error
1116
1139
  end
1117
1140
 
1118
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
1119
- @instrumenter.instrument(
1141
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1142
+ instrumenter.instrument(
1120
1143
  "sql.active_record",
1121
1144
  sql: sql,
1122
1145
  name: name,
1123
1146
  binds: binds,
1124
1147
  type_casted_binds: type_casted_binds,
1125
- statement_name: statement_name,
1126
1148
  async: async,
1127
1149
  connection: self,
1128
1150
  transaction: current_transaction.user_transaction.presence,
@@ -1133,11 +1155,8 @@ module ActiveRecord
1133
1155
  raise ex.set_query(sql, binds)
1134
1156
  end
1135
1157
 
1136
- def transform_query(sql)
1137
- ActiveRecord.query_transformers.each do |transformer|
1138
- sql = transformer.call(sql, self)
1139
- end
1140
- sql
1158
+ def instrumenter # :nodoc:
1159
+ ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] ||= ActiveSupport::Notifications.instrumenter
1141
1160
  end
1142
1161
 
1143
1162
  def translate_exception(exception, message:, sql:, binds:)
@@ -1150,10 +1169,6 @@ module ActiveRecord
1150
1169
  end
1151
1170
  end
1152
1171
 
1153
- def without_prepared_statement?(binds)
1154
- !prepared_statements || binds.empty?
1155
- end
1156
-
1157
1172
  def column_for(table_name, column_name)
1158
1173
  column_name = column_name.to_s
1159
1174
  columns(table_name).detect { |c| c.name == column_name } ||
@@ -1205,6 +1220,13 @@ module ActiveRecord
1205
1220
  check_version
1206
1221
  end
1207
1222
 
1223
+ def attempt_configure_connection
1224
+ yield
1225
+ rescue Exception # Need to handle things such as Timeout::ExitException
1226
+ disconnect!
1227
+ raise
1228
+ end
1229
+
1208
1230
  def default_prepared_statements
1209
1231
  true
1210
1232
  end