activerecord 8.0.5 → 8.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +421 -652
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/belongs_to_association.rb +0 -2
  7. data/lib/active_record/associations/builder/association.rb +16 -5
  8. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  9. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  10. data/lib/active_record/associations/builder/has_one.rb +1 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  12. data/lib/active_record/associations/collection_proxy.rb +22 -4
  13. data/lib/active_record/associations/deprecation.rb +88 -0
  14. data/lib/active_record/associations/errors.rb +3 -0
  15. data/lib/active_record/associations/join_dependency.rb +4 -2
  16. data/lib/active_record/associations/preloader/batch.rb +1 -7
  17. data/lib/active_record/associations/preloader/branch.rb +1 -0
  18. data/lib/active_record/associations.rb +159 -21
  19. data/lib/active_record/attribute_methods/serialization.rb +16 -3
  20. data/lib/active_record/attribute_methods.rb +1 -1
  21. data/lib/active_record/attributes.rb +3 -0
  22. data/lib/active_record/autosave_association.rb +2 -2
  23. data/lib/active_record/base.rb +2 -3
  24. data/lib/active_record/coders/json.rb +14 -5
  25. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
  26. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +383 -52
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +27 -31
  30. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
  31. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  32. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
  34. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  35. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +88 -25
  36. data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
  37. data/lib/active_record/connection_adapters/abstract_adapter.rb +96 -49
  38. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
  39. data/lib/active_record/connection_adapters/column.rb +17 -4
  40. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  41. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  42. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  43. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
  44. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
  45. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  46. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -4
  47. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
  48. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  49. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  50. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  51. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
  52. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
  53. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
  54. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
  55. data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -12
  56. data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
  57. data/lib/active_record/connection_adapters/sqlite3/column.rb +2 -8
  58. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
  59. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  60. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -15
  61. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
  62. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -33
  63. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
  64. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  65. data/lib/active_record/connection_adapters.rb +1 -0
  66. data/lib/active_record/connection_handling.rb +8 -12
  67. data/lib/active_record/core.rb +5 -4
  68. data/lib/active_record/counter_cache.rb +33 -8
  69. data/lib/active_record/database_configurations/database_config.rb +5 -1
  70. data/lib/active_record/database_configurations/hash_config.rb +50 -9
  71. data/lib/active_record/database_configurations/url_config.rb +13 -3
  72. data/lib/active_record/database_configurations.rb +7 -3
  73. data/lib/active_record/delegated_type.rb +1 -1
  74. data/lib/active_record/dynamic_matchers.rb +54 -69
  75. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  76. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  77. data/lib/active_record/encryption/scheme.rb +1 -1
  78. data/lib/active_record/enum.rb +24 -8
  79. data/lib/active_record/errors.rb +23 -7
  80. data/lib/active_record/explain_registry.rb +0 -1
  81. data/lib/active_record/filter_attribute_handler.rb +73 -0
  82. data/lib/active_record/fixtures.rb +2 -2
  83. data/lib/active_record/future_result.rb +0 -2
  84. data/lib/active_record/gem_version.rb +3 -3
  85. data/lib/active_record/inheritance.rb +1 -1
  86. data/lib/active_record/insert_all.rb +14 -9
  87. data/lib/active_record/locking/optimistic.rb +7 -0
  88. data/lib/active_record/locking/pessimistic.rb +5 -0
  89. data/lib/active_record/log_subscriber.rb +1 -5
  90. data/lib/active_record/middleware/shard_selector.rb +34 -17
  91. data/lib/active_record/migration/command_recorder.rb +15 -2
  92. data/lib/active_record/migration/compatibility.rb +34 -24
  93. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  94. data/lib/active_record/migration.rb +26 -16
  95. data/lib/active_record/model_schema.rb +10 -7
  96. data/lib/active_record/nested_attributes.rb +2 -0
  97. data/lib/active_record/persistence.rb +34 -3
  98. data/lib/active_record/query_cache.rb +22 -15
  99. data/lib/active_record/query_logs.rb +3 -7
  100. data/lib/active_record/railtie.rb +32 -3
  101. data/lib/active_record/railties/databases.rake +16 -4
  102. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  103. data/lib/active_record/railties/job_runtime.rb +10 -11
  104. data/lib/active_record/reflection.rb +42 -3
  105. data/lib/active_record/relation/batches.rb +26 -12
  106. data/lib/active_record/relation/calculations.rb +20 -9
  107. data/lib/active_record/relation/delegation.rb +0 -1
  108. data/lib/active_record/relation/finder_methods.rb +28 -12
  109. data/lib/active_record/relation/merger.rb +2 -2
  110. data/lib/active_record/relation/predicate_builder/array_handler.rb +1 -3
  111. data/lib/active_record/relation/predicate_builder.rb +2 -2
  112. data/lib/active_record/relation/query_attribute.rb +3 -1
  113. data/lib/active_record/relation/query_methods.rb +40 -31
  114. data/lib/active_record/relation/where_clause.rb +2 -11
  115. data/lib/active_record/relation.rb +26 -14
  116. data/lib/active_record/result.rb +44 -21
  117. data/lib/active_record/sanitization.rb +2 -0
  118. data/lib/active_record/schema_dumper.rb +12 -10
  119. data/lib/active_record/scoping.rb +0 -1
  120. data/lib/active_record/signed_id.rb +43 -15
  121. data/lib/active_record/statement_cache.rb +13 -9
  122. data/lib/active_record/store.rb +44 -19
  123. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  124. data/lib/active_record/tasks/database_tasks.rb +2 -21
  125. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
  126. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
  127. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  128. data/lib/active_record/test_databases.rb +10 -2
  129. data/lib/active_record/test_fixtures.rb +27 -2
  130. data/lib/active_record/testing/query_assertions.rb +8 -2
  131. data/lib/active_record/timestamp.rb +4 -2
  132. data/lib/active_record/transaction.rb +2 -5
  133. data/lib/active_record/transactions.rb +32 -10
  134. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  135. data/lib/active_record/type/internal/timezone.rb +7 -0
  136. data/lib/active_record/type/json.rb +15 -2
  137. data/lib/active_record/type/serialized.rb +11 -9
  138. data/lib/active_record/type/type_map.rb +1 -1
  139. data/lib/active_record/type_caster/connection.rb +2 -1
  140. data/lib/active_record/validations/associated.rb +1 -1
  141. data/lib/active_record.rb +65 -3
  142. data/lib/arel/alias_predication.rb +2 -0
  143. data/lib/arel/crud.rb +6 -11
  144. data/lib/arel/nodes/count.rb +2 -2
  145. data/lib/arel/nodes/function.rb +4 -10
  146. data/lib/arel/nodes/named_function.rb +2 -2
  147. data/lib/arel/nodes/node.rb +1 -1
  148. data/lib/arel/nodes.rb +0 -2
  149. data/lib/arel/predications.rb +3 -1
  150. data/lib/arel/select_manager.rb +7 -2
  151. data/lib/arel/visitors/dot.rb +0 -3
  152. data/lib/arel/visitors/postgresql.rb +55 -0
  153. data/lib/arel/visitors/sqlite.rb +55 -8
  154. data/lib/arel/visitors/to_sql.rb +3 -21
  155. data/lib/arel.rb +3 -1
  156. metadata +14 -10
  157. data/lib/active_record/normalization.rb +0 -163
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/access"
4
+ require "active_support/core_ext/string/filters"
4
5
  require "openssl"
5
6
 
6
7
  module ActiveRecord
@@ -99,7 +100,7 @@ module ActiveRecord
99
100
  # # Check a valid index exists (PostgreSQL only)
100
101
  # index_exists?(:suppliers, :company_id, valid: true)
101
102
  #
102
- def index_exists?(table_name, column_name, **options)
103
+ def index_exists?(table_name, column_name = nil, **options)
103
104
  indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
104
105
  end
105
106
 
@@ -357,11 +358,13 @@ module ActiveRecord
357
358
  # # Creates a table called 'music_artists_records' with no id.
358
359
  # create_join_table('music_artists', 'music_records')
359
360
  #
361
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference]
362
+ # for details of the options you can use in +column_options+. +column_options+
363
+ # will be applied to both columns.
364
+ #
360
365
  # You can pass an +options+ hash which can include the following keys:
361
366
  # [<tt>:table_name</tt>]
362
367
  # Sets the table name, overriding the default.
363
- # [<tt>:column_options</tt>]
364
- # Any extra options you want appended to the columns definition.
365
368
  # [<tt>:options</tt>]
366
369
  # Any extra options you want appended to the table definition.
367
370
  # [<tt>:temporary</tt>]
@@ -378,6 +381,19 @@ module ActiveRecord
378
381
  # t.index :category_id
379
382
  # end
380
383
  #
384
+ # ====== Add foreign keys with delete cascade
385
+ #
386
+ # create_join_table(:assemblies, :parts, column_options: { foreign_key: { on_delete: :cascade } })
387
+ #
388
+ # generates:
389
+ #
390
+ # CREATE TABLE assemblies_parts (
391
+ # assembly_id bigint NOT NULL,
392
+ # part_id bigint NOT NULL,
393
+ # CONSTRAINT fk_rails_0d8a572d89 FOREIGN KEY ("assembly_id") REFERENCES "assemblies" ("id") ON DELETE CASCADE,
394
+ # CONSTRAINT fk_rails_ec7b48402b FOREIGN KEY ("part_id") REFERENCES "parts" ("id") ON DELETE CASCADE
395
+ # )
396
+ #
381
397
  # ====== Add a backend specific option to the generated SQL (MySQL)
382
398
  #
383
399
  # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
@@ -915,6 +931,19 @@ module ActiveRecord
915
931
  # Concurrently adding an index is not supported in a transaction.
916
932
  #
917
933
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
934
+ #
935
+ # ====== Creating an index that is not used by queries
936
+ #
937
+ # add_index(:developers, :name, enabled: false)
938
+ #
939
+ # generates:
940
+ #
941
+ # CREATE INDEX index_developers_on_name ON developers (name) INVISIBLE -- MySQL
942
+ #
943
+ # CREATE INDEX index_developers_on_name ON developers (name) IGNORED -- MariaDB
944
+ #
945
+ # Note: only supported by MySQL version 8.0.0 and greater, and MariaDB version 10.6.0 and greater.
946
+ #
918
947
  def add_index(table_name, column_name, **options)
919
948
  create_index = build_create_index_definition(table_name, column_name, **options)
920
949
  execute schema_creation.accept(create_index)
@@ -1175,9 +1204,10 @@ module ActiveRecord
1175
1204
  # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1176
1205
  def add_foreign_key(from_table, to_table, **options)
1177
1206
  return unless use_foreign_keys?
1178
- return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1179
1207
 
1180
1208
  options = foreign_key_options(from_table, to_table, options)
1209
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column, :primary_key))
1210
+
1181
1211
  at = create_alter_table from_table
1182
1212
  at.add_foreign_key to_table, options
1183
1213
 
@@ -1216,7 +1246,7 @@ module ActiveRecord
1216
1246
  # The name of the table that contains the referenced primary key.
1217
1247
  def remove_foreign_key(from_table, to_table = nil, **options)
1218
1248
  return unless use_foreign_keys?
1219
- return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1249
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table, **options.slice(:column))
1220
1250
 
1221
1251
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1222
1252
 
@@ -1355,7 +1385,7 @@ module ActiveRecord
1355
1385
  execute schema_creation.accept(at)
1356
1386
  end
1357
1387
 
1358
- def dump_schema_information # :nodoc:
1388
+ def dump_schema_versions # :nodoc:
1359
1389
  versions = pool.schema_migration.versions
1360
1390
  insert_versions_sql(versions) if versions.any?
1361
1391
  end
@@ -1477,7 +1507,7 @@ module ActiveRecord
1477
1507
  end
1478
1508
 
1479
1509
  def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1480
- options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1510
+ options.assert_valid_keys(valid_index_options)
1481
1511
 
1482
1512
  column_names = index_column_names(column_name)
1483
1513
 
@@ -1486,7 +1516,7 @@ module ActiveRecord
1486
1516
 
1487
1517
  validate_index_length!(table_name, index_name, internal)
1488
1518
 
1489
- index = IndexDefinition.new(
1519
+ index = create_index_definition(
1490
1520
  table_name, index_name,
1491
1521
  options[:unique],
1492
1522
  column_names,
@@ -1541,6 +1571,20 @@ module ActiveRecord
1541
1571
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1542
1572
  end
1543
1573
 
1574
+ # Enables an index to be used by queries.
1575
+ #
1576
+ # enable_index(:users, :email)
1577
+ def enable_index(table_name, index_name)
1578
+ raise NotImplementedError, "#{self.class} does not support enabling indexes"
1579
+ end
1580
+
1581
+ # Prevents an index from being used by queries.
1582
+ #
1583
+ # disable_index(:users, :email)
1584
+ def disable_index(table_name, index_name)
1585
+ raise NotImplementedError, "#{self.class} does not support disabling indexes"
1586
+ end
1587
+
1544
1588
  def create_schema_dumper(options) # :nodoc:
1545
1589
  SchemaDumper.create(self, options)
1546
1590
  end
@@ -1560,11 +1604,11 @@ module ActiveRecord
1560
1604
  non_combinable_operations = []
1561
1605
 
1562
1606
  operations.each do |command, args|
1563
- args.shift # remove table_name
1607
+ table, arguments = args.shift, args
1564
1608
  method = :"#{command}_for_alter"
1565
1609
 
1566
1610
  if respond_to?(method, true)
1567
- sqls, procs = Array(send(method, table_name, *args)).partition { |v| v.is_a?(String) }
1611
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1568
1612
  sql_fragments.concat(sqls)
1569
1613
  non_combinable_operations.concat(procs)
1570
1614
  else
@@ -1572,7 +1616,7 @@ module ActiveRecord
1572
1616
  non_combinable_operations.each(&:call)
1573
1617
  sql_fragments = []
1574
1618
  non_combinable_operations = []
1575
- send(command, table_name, *args)
1619
+ send(command, table, *arguments)
1576
1620
  end
1577
1621
  end
1578
1622
 
@@ -1607,7 +1651,7 @@ module ActiveRecord
1607
1651
  name = "idx_on_#{Array(column) * '_'}"
1608
1652
 
1609
1653
  short_limit = max_index_name_size - hashed_identifier.bytesize
1610
- short_name = name.mb_chars.limit(short_limit).to_s
1654
+ short_name = name.truncate_bytes(short_limit, omission: nil)
1611
1655
 
1612
1656
  "#{short_name}#{hashed_identifier}"
1613
1657
  end
@@ -1629,6 +1673,10 @@ module ActiveRecord
1629
1673
  end
1630
1674
  end
1631
1675
 
1676
+ def valid_index_options
1677
+ [:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct]
1678
+ end
1679
+
1632
1680
  def options_for_index_columns(options)
1633
1681
  if options.is_a?(Hash)
1634
1682
  options.symbolize_keys
@@ -1705,6 +1753,10 @@ module ActiveRecord
1705
1753
  TableDefinition.new(self, name, **options)
1706
1754
  end
1707
1755
 
1756
+ def create_index_definition(table_name, name, unique, columns, **options)
1757
+ IndexDefinition.new(table_name, name, unique, columns, **options)
1758
+ end
1759
+
1708
1760
  def create_alter_table(name)
1709
1761
  AlterTable.new create_table_definition(name)
1710
1762
  end
@@ -1767,7 +1819,20 @@ module ActiveRecord
1767
1819
 
1768
1820
  def foreign_key_for(from_table, **options)
1769
1821
  return unless use_foreign_keys?
1770
- foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1822
+
1823
+ keys = foreign_keys(from_table)
1824
+
1825
+ if options[:_skip_column_match]
1826
+ return keys.find { |fk| fk.defined_for?(**options) }
1827
+ end
1828
+
1829
+ if options[:column].nil?
1830
+ default_column = foreign_key_column_for(options[:to_table], "id")
1831
+ matches = keys.select { |fk| fk.column == default_column }
1832
+ keys = matches if matches.any?
1833
+ end
1834
+
1835
+ keys.find { |fk| fk.defined_for?(**options) }
1771
1836
  end
1772
1837
 
1773
1838
  def foreign_key_for!(from_table, to_table: nil, **options)
@@ -1810,13 +1875,19 @@ module ActiveRecord
1810
1875
 
1811
1876
  def validate_index_length!(table_name, new_name, internal = false)
1812
1877
  if new_name.length > index_name_length
1813
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
1878
+ raise ArgumentError, <<~MSG.squish
1879
+ Index name '#{new_name}' on table '#{table_name}' is too long (#{new_name.length} characters); the limit
1880
+ is #{index_name_length} characters
1881
+ MSG
1814
1882
  end
1815
1883
  end
1816
1884
 
1817
1885
  def validate_table_length!(table_name)
1818
1886
  if table_name.length > table_name_length
1819
- raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1887
+ raise ArgumentError, <<~MSG.squish
1888
+ Table name '#{table_name}' is too long (#{table_name.length} characters); the limit is
1889
+ #{table_name_length} characters
1890
+ MSG
1820
1891
  end
1821
1892
  end
1822
1893
 
@@ -1878,16 +1949,8 @@ module ActiveRecord
1878
1949
  end
1879
1950
 
1880
1951
  def insert_versions_sql(versions)
1881
- sm_table = quote_table_name(pool.schema_migration.table_name)
1882
-
1883
- if versions.is_a?(Array)
1884
- sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1885
- sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1886
- sql << ";"
1887
- sql
1888
- else
1889
- "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1890
- end
1952
+ versions_formatter = ActiveRecord.schema_versions_formatter.new(self)
1953
+ versions_formatter.format(versions)
1891
1954
  end
1892
1955
 
1893
1956
  def data_source_sql(name = nil, type: nil)
@@ -112,6 +112,7 @@ module ActiveRecord
112
112
  def closed?; true; end
113
113
  def open?; false; end
114
114
  def joinable?; false; end
115
+ def isolation; nil; end
115
116
  def add_record(record, _ = true); end
116
117
  def restartable?; false; end
117
118
  def dirty?; false; end
@@ -150,6 +151,11 @@ module ActiveRecord
150
151
 
151
152
  delegate :invalidate!, :invalidated?, to: :@state
152
153
 
154
+ # Returns the isolation level if it was explicitly set, nil otherwise
155
+ def isolation
156
+ @isolation_level
157
+ end
158
+
153
159
  def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
154
160
  super()
155
161
  @connection = connection
@@ -175,11 +181,11 @@ module ActiveRecord
175
181
  end
176
182
 
177
183
  def open?
178
- true
184
+ !closed?
179
185
  end
180
186
 
181
187
  def closed?
182
- false
188
+ @state.finalized?
183
189
  end
184
190
 
185
191
  def add_record(record, ensure_finalize = true)
@@ -386,7 +392,7 @@ module ActiveRecord
386
392
  @parent.state.add_child(@state)
387
393
  end
388
394
 
389
- delegate :materialize!, :materialized?, :restart, to: :@parent
395
+ delegate :materialize!, :materialized?, :restart, :isolation, to: :@parent
390
396
 
391
397
  def rollback
392
398
  @state.rollback!
@@ -405,6 +411,7 @@ module ActiveRecord
405
411
  def initialize(connection, savepoint_name, parent_transaction, **options)
406
412
  super(connection, **options)
407
413
 
414
+ @parent_transaction = parent_transaction
408
415
  parent_transaction.state.add_child(@state)
409
416
 
410
417
  if isolation_level
@@ -414,6 +421,11 @@ module ActiveRecord
414
421
  @savepoint_name = savepoint_name
415
422
  end
416
423
 
424
+ # Delegates to parent transaction's isolation level
425
+ def isolation
426
+ @parent_transaction.isolation
427
+ end
428
+
417
429
  def materialize!
418
430
  connection.create_savepoint(savepoint_name)
419
431
  super
@@ -620,6 +632,7 @@ module ActiveRecord
620
632
  end
621
633
 
622
634
  def within_new_transaction(isolation: nil, joinable: true)
635
+ isolation ||= @connection.pool.pool_transaction_isolation_level
623
636
  @connection.lock.synchronize do
624
637
  transaction = begin_transaction(isolation: isolation, joinable: joinable)
625
638
  begin
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
 
43
43
  attr_reader :pool
44
44
  attr_reader :visitor, :owner, :logger, :lock
45
- attr_accessor :pinned # :nodoc:
45
+ attr_accessor :allow_preconnect
46
46
  alias :in_use? :owner
47
47
 
48
48
  def pool=(value)
@@ -120,7 +120,7 @@ module ActiveRecord
120
120
 
121
121
  # Opens a database console session.
122
122
  def self.dbconsole(config, options = {})
123
- raise NotImplementedError
123
+ raise NotImplementedError.new("#{self.class} should define `dbconsole` that accepts a db config and options to implement connecting to the db console")
124
124
  end
125
125
 
126
126
  def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
@@ -128,6 +128,7 @@ module ActiveRecord
128
128
 
129
129
  @raw_connection = nil
130
130
  @unconfigured_connection = nil
131
+ @connected_since = nil
131
132
 
132
133
  if config_or_deprecated_connection.is_a?(Hash)
133
134
  @config = config_or_deprecated_connection.symbolize_keys
@@ -140,6 +141,7 @@ module ActiveRecord
140
141
  # Soft-deprecated for now; we'll probably warn in future.
141
142
 
142
143
  @unconfigured_connection = config_or_deprecated_connection
144
+ @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
143
145
  @logger = deprecated_logger || ActiveRecord::Base.logger
144
146
  if deprecated_config
145
147
  @config = (deprecated_config || {}).symbolize_keys
@@ -151,9 +153,9 @@ module ActiveRecord
151
153
  end
152
154
 
153
155
  @owner = nil
154
- @pinned = false
155
156
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
156
157
  @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
158
+ @allow_preconnect = true
157
159
  @visitor = arel_visitor
158
160
  @statements = build_statement_pool
159
161
  self.lock_thread = nil
@@ -171,6 +173,8 @@ module ActiveRecord
171
173
  @raw_connection_dirty = false
172
174
  @last_activity = nil
173
175
  @verified = false
176
+
177
+ @pool_jitter = rand * max_jitter
174
178
  end
175
179
 
176
180
  def inspect # :nodoc:
@@ -198,6 +202,11 @@ module ActiveRecord
198
202
  end
199
203
  end
200
204
 
205
+ MAX_JITTER = 0.0..1.0 # :nodoc:
206
+ def max_jitter
207
+ (@config[:pool_jitter] || 0.2).to_f.clamp(MAX_JITTER)
208
+ end
209
+
201
210
  def replica?
202
211
  @config[:replica] || false
203
212
  end
@@ -262,7 +271,11 @@ module ActiveRecord
262
271
  end
263
272
 
264
273
  def valid_type?(type) # :nodoc:
265
- !native_database_types[type].nil?
274
+ self.class.valid_type?(type)
275
+ end
276
+
277
+ def native_database_types # :nodoc:
278
+ self.class.native_database_types
266
279
  end
267
280
 
268
281
  # this method must only be called while holding connection pool's mutex
@@ -301,8 +314,12 @@ module ActiveRecord
301
314
  @pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
302
315
  end
303
316
 
317
+ def pool_jitter(duration)
318
+ duration * (1.0 - @pool_jitter)
319
+ end
320
+
304
321
  # this method must only be called while holding connection pool's mutex
305
- def expire
322
+ def expire(update_idle = true) # :nodoc:
306
323
  if in_use?
307
324
  if @owner != ActiveSupport::IsolatedExecutionState.context
308
325
  raise ActiveRecordError, "Cannot expire connection, " \
@@ -310,7 +327,7 @@ module ActiveRecord
310
327
  "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
311
328
  end
312
329
 
313
- @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
330
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC) if update_idle
314
331
  @owner = nil
315
332
  else
316
333
  raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
@@ -343,6 +360,21 @@ module ActiveRecord
343
360
  end
344
361
  end
345
362
 
363
+ # Seconds since this connection was established. nil if not
364
+ # connected; infinity if the connection has been explicitly
365
+ # retired.
366
+ def connection_age # :nodoc:
367
+ if @raw_connection && @connected_since
368
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @connected_since
369
+ end
370
+ end
371
+
372
+ # Mark the connection as needing to be retired, as if the age has
373
+ # exceeded the maximum allowed.
374
+ def force_retirement # :nodoc:
375
+ @connected_since &&= -Float::INFINITY
376
+ end
377
+
346
378
  def unprepared_statement
347
379
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
348
380
  yield
@@ -557,6 +589,10 @@ module ActiveRecord
557
589
  false
558
590
  end
559
591
 
592
+ def supports_disabling_indexes?
593
+ false
594
+ end
595
+
560
596
  def return_value_after_insert?(column) # :nodoc:
561
597
  column.auto_populated?
562
598
  end
@@ -666,33 +702,37 @@ module ActiveRecord
666
702
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
667
703
 
668
704
  @lock.synchronize do
669
- attempt_configure_connection do
670
- reconnect
705
+ @allow_preconnect = false
671
706
 
672
- enable_lazy_transactions!
673
- @raw_connection_dirty = false
674
- @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
675
- @verified = true
707
+ reconnect
676
708
 
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)
709
+ enable_lazy_transactions!
710
+ @raw_connection_dirty = false
711
+ @last_activity = @connected_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
712
+ @verified = true
713
+ @allow_preconnect = true
714
+
715
+ reset_transaction(restore: restore_transactions) do
716
+ clear_cache!(new_connection: true)
717
+ attempt_configure_connection
718
+ end
719
+ rescue => original_exception
720
+ translated_exception = translate_exception_class(original_exception, nil, nil)
721
+ retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
684
722
 
685
- if !retry_deadline_exceeded && retries_available > 0
686
- retries_available -= 1
723
+ if !retry_deadline_exceeded && retries_available > 0
724
+ retries_available -= 1
687
725
 
688
- if retryable_connection_error?(translated_exception)
689
- backoff(connection_retries - retries_available)
690
- retry
691
- end
726
+ if retryable_connection_error?(translated_exception)
727
+ backoff(connection_retries - retries_available)
728
+ retry
692
729
  end
693
-
694
- raise translated_exception
695
730
  end
731
+
732
+ @last_activity = nil
733
+ @verified = false
734
+
735
+ raise translated_exception
696
736
  end
697
737
  end
698
738
 
@@ -700,11 +740,10 @@ module ActiveRecord
700
740
  # method does nothing.
701
741
  def disconnect!
702
742
  @lock.synchronize do
703
- @last_activity = nil
704
- @verified = false
705
743
  clear_cache!(new_connection: true)
706
744
  reset_transaction
707
745
  @raw_connection_dirty = false
746
+ @connected_since = nil
708
747
  end
709
748
  end
710
749
 
@@ -727,11 +766,9 @@ module ActiveRecord
727
766
  # should call super immediately after resetting the connection (and while
728
767
  # still holding @lock).
729
768
  def reset!
730
- attempt_configure_connection do
731
- clear_cache!(new_connection: true)
732
- reset_transaction
733
- configure_connection
734
- end
769
+ clear_cache!(new_connection: true)
770
+ reset_transaction
771
+ attempt_configure_connection
735
772
  end
736
773
 
737
774
  # Removes the connection from the pool and disconnect it.
@@ -765,13 +802,12 @@ module ActiveRecord
765
802
  unless active?
766
803
  @lock.synchronize do
767
804
  if @unconfigured_connection
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
805
+ @raw_connection = @unconfigured_connection
806
+ @unconfigured_connection = nil
807
+ attempt_configure_connection
808
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
809
+ @verified = true
810
+ @allow_preconnect = true
775
811
  return
776
812
  end
777
813
 
@@ -779,6 +815,7 @@ module ActiveRecord
779
815
  end
780
816
  end
781
817
 
818
+ @last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
782
819
  @verified = true
783
820
  end
784
821
 
@@ -792,6 +829,10 @@ module ActiveRecord
792
829
  @verified = nil
793
830
  end
794
831
 
832
+ def verified? # :nodoc:
833
+ @verified
834
+ end
835
+
795
836
  # Provides access to the underlying database driver for this adapter. For
796
837
  # example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
797
838
  # and a PG::Connection object in case of PostgreSQLAdapter.
@@ -877,7 +918,7 @@ module ActiveRecord
877
918
  def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
878
919
  mapping.register_type(key) do |*args|
879
920
  precision = extract_precision(args.last)
880
- klass.new(precision: precision, **kwargs)
921
+ klass.new(precision: precision, **kwargs).freeze
881
922
  end
882
923
  end
883
924
 
@@ -889,6 +930,10 @@ module ActiveRecord
889
930
  end
890
931
  end
891
932
 
933
+ def valid_type?(type) # :nodoc:
934
+ !native_database_types[type].nil?
935
+ end
936
+
892
937
  private
893
938
  def initialize_type_map(m)
894
939
  register_class_with_limit m, %r(boolean)i, Type::Boolean
@@ -908,7 +953,7 @@ module ActiveRecord
908
953
  m.alias_type %r(number)i, "decimal"
909
954
  m.alias_type %r(double)i, "float"
910
955
 
911
- m.register_type %r(^json)i, Type::Json.new
956
+ m.register_type %r(^json)i, Type::Json.new.freeze
912
957
 
913
958
  m.register_type(%r(decimal)i) do |sql_type|
914
959
  scale = extract_scale(sql_type)
@@ -916,9 +961,9 @@ module ActiveRecord
916
961
 
917
962
  if scale == 0
918
963
  # FIXME: Remove this class as well
919
- Type::DecimalWithoutScale.new(precision: precision)
964
+ Type::DecimalWithoutScale.new(precision: precision).freeze
920
965
  else
921
- Type::Decimal.new(precision: precision, scale: scale)
966
+ Type::Decimal.new(precision: precision, scale: scale).freeze
922
967
  end
923
968
  end
924
969
  end
@@ -926,7 +971,7 @@ module ActiveRecord
926
971
  def register_class_with_limit(mapping, key, klass)
927
972
  mapping.register_type(key) do |*args|
928
973
  limit = extract_limit(args.last)
929
- klass.new(limit: limit)
974
+ klass.new(limit: limit).freeze
930
975
  end
931
976
  end
932
977
 
@@ -1087,7 +1132,7 @@ module ActiveRecord
1087
1132
  end
1088
1133
 
1089
1134
  def reconnect
1090
- raise NotImplementedError
1135
+ raise NotImplementedError.new("#{self.class} should define `reconnect` to implement adapter-specific logic for reconnecting to the database")
1091
1136
  end
1092
1137
 
1093
1138
  # Returns a raw connection for internal use with methods that are known
@@ -1138,7 +1183,7 @@ module ActiveRecord
1138
1183
  active_record_error
1139
1184
  end
1140
1185
 
1141
- def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
1186
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, allow_retry: false, &block) # :doc:
1142
1187
  instrumenter.instrument(
1143
1188
  "sql.active_record",
1144
1189
  sql: sql,
@@ -1146,8 +1191,10 @@ module ActiveRecord
1146
1191
  binds: binds,
1147
1192
  type_casted_binds: type_casted_binds,
1148
1193
  async: async,
1194
+ allow_retry: allow_retry,
1149
1195
  connection: self,
1150
1196
  transaction: current_transaction.user_transaction.presence,
1197
+ affected_rows: 0,
1151
1198
  row_count: 0,
1152
1199
  &block
1153
1200
  )
@@ -1221,7 +1268,7 @@ module ActiveRecord
1221
1268
  end
1222
1269
 
1223
1270
  def attempt_configure_connection
1224
- yield
1271
+ configure_connection
1225
1272
  rescue Exception # Need to handle things such as Timeout::ExitException
1226
1273
  disconnect!
1227
1274
  raise