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
@@ -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
 
@@ -186,6 +187,9 @@ module ActiveRecord
186
187
  # 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
188
  #
188
189
  # A Symbol can be used to specify the type of the generated primary key column.
190
+ #
191
+ # A Hash can be used to specify the generated primary key column creation options.
192
+ # See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
189
193
  # [<tt>:primary_key</tt>]
190
194
  # The name of the primary key, if one is to be added automatically.
191
195
  # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
@@ -354,11 +358,13 @@ module ActiveRecord
354
358
  # # Creates a table called 'music_artists_records' with no id.
355
359
  # create_join_table('music_artists', 'music_records')
356
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
+ #
357
365
  # You can pass an +options+ hash which can include the following keys:
358
366
  # [<tt>:table_name</tt>]
359
367
  # Sets the table name, overriding the default.
360
- # [<tt>:column_options</tt>]
361
- # Any extra options you want appended to the columns definition.
362
368
  # [<tt>:options</tt>]
363
369
  # Any extra options you want appended to the table definition.
364
370
  # [<tt>:temporary</tt>]
@@ -375,6 +381,19 @@ module ActiveRecord
375
381
  # t.index :category_id
376
382
  # end
377
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
+ #
378
397
  # ====== Add a backend specific option to the generated SQL (MySQL)
379
398
  #
380
399
  # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
@@ -548,7 +567,7 @@ module ActiveRecord
548
567
  #
549
568
  # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
550
569
  #
551
- # The +type+ parameter is normally one of the migrations native types,
570
+ # The +type+ parameter is normally one of the migration's native types,
552
571
  # which is one of the following:
553
572
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
554
573
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
@@ -912,6 +931,19 @@ module ActiveRecord
912
931
  # Concurrently adding an index is not supported in a transaction.
913
932
  #
914
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
+ #
915
947
  def add_index(table_name, column_name, **options)
916
948
  create_index = build_create_index_definition(table_name, column_name, **options)
917
949
  execute schema_creation.accept(create_index)
@@ -985,7 +1017,7 @@ module ActiveRecord
985
1017
  # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
986
1018
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
987
1019
  return unless old_index_def
988
- add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
1020
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique, where: old_index_def.where)
989
1021
  remove_index(table_name, name: old_name)
990
1022
  end
991
1023
 
@@ -1172,9 +1204,10 @@ module ActiveRecord
1172
1204
  # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1173
1205
  def add_foreign_key(from_table, to_table, **options)
1174
1206
  return unless use_foreign_keys?
1175
- return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1176
1207
 
1177
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
+
1178
1211
  at = create_alter_table from_table
1179
1212
  at.add_foreign_key to_table, options
1180
1213
 
@@ -1213,7 +1246,7 @@ module ActiveRecord
1213
1246
  # The name of the table that contains the referenced primary key.
1214
1247
  def remove_foreign_key(from_table, to_table = nil, **options)
1215
1248
  return unless use_foreign_keys?
1216
- 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))
1217
1250
 
1218
1251
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1219
1252
 
@@ -1324,7 +1357,7 @@ module ActiveRecord
1324
1357
  def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1325
1358
  return unless supports_check_constraints?
1326
1359
 
1327
- return if if_exists && !check_constraint_exists?(table_name, **options)
1360
+ return if if_exists && !check_constraint_exists?(table_name, expression: expression, **options)
1328
1361
 
1329
1362
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1330
1363
 
@@ -1352,7 +1385,7 @@ module ActiveRecord
1352
1385
  execute schema_creation.accept(at)
1353
1386
  end
1354
1387
 
1355
- def dump_schema_information # :nodoc:
1388
+ def dump_schema_versions # :nodoc:
1356
1389
  versions = pool.schema_migration.versions
1357
1390
  insert_versions_sql(versions) if versions.any?
1358
1391
  end
@@ -1474,7 +1507,7 @@ module ActiveRecord
1474
1507
  end
1475
1508
 
1476
1509
  def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1477
- 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)
1478
1511
 
1479
1512
  column_names = index_column_names(column_name)
1480
1513
 
@@ -1483,7 +1516,7 @@ module ActiveRecord
1483
1516
 
1484
1517
  validate_index_length!(table_name, index_name, internal)
1485
1518
 
1486
- index = IndexDefinition.new(
1519
+ index = create_index_definition(
1487
1520
  table_name, index_name,
1488
1521
  options[:unique],
1489
1522
  column_names,
@@ -1538,6 +1571,20 @@ module ActiveRecord
1538
1571
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1539
1572
  end
1540
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
+
1541
1588
  def create_schema_dumper(options) # :nodoc:
1542
1589
  SchemaDumper.create(self, options)
1543
1590
  end
@@ -1557,11 +1604,11 @@ module ActiveRecord
1557
1604
  non_combinable_operations = []
1558
1605
 
1559
1606
  operations.each do |command, args|
1560
- table, arguments = args.shift, args
1607
+ args.shift # remove table_name
1561
1608
  method = :"#{command}_for_alter"
1562
1609
 
1563
1610
  if respond_to?(method, true)
1564
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1611
+ sqls, procs = Array(send(method, table_name, *args)).partition { |v| v.is_a?(String) }
1565
1612
  sql_fragments.concat(sqls)
1566
1613
  non_combinable_operations.concat(procs)
1567
1614
  else
@@ -1569,7 +1616,7 @@ module ActiveRecord
1569
1616
  non_combinable_operations.each(&:call)
1570
1617
  sql_fragments = []
1571
1618
  non_combinable_operations = []
1572
- send(command, table, *arguments)
1619
+ send(command, table_name, *args)
1573
1620
  end
1574
1621
  end
1575
1622
 
@@ -1604,7 +1651,7 @@ module ActiveRecord
1604
1651
  name = "idx_on_#{Array(column) * '_'}"
1605
1652
 
1606
1653
  short_limit = max_index_name_size - hashed_identifier.bytesize
1607
- short_name = name.mb_chars.limit(short_limit).to_s
1654
+ short_name = name.truncate_bytes(short_limit, omission: nil)
1608
1655
 
1609
1656
  "#{short_name}#{hashed_identifier}"
1610
1657
  end
@@ -1626,6 +1673,10 @@ module ActiveRecord
1626
1673
  end
1627
1674
  end
1628
1675
 
1676
+ def valid_index_options
1677
+ [:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct]
1678
+ end
1679
+
1629
1680
  def options_for_index_columns(options)
1630
1681
  if options.is_a?(Hash)
1631
1682
  options.symbolize_keys
@@ -1702,6 +1753,10 @@ module ActiveRecord
1702
1753
  TableDefinition.new(self, name, **options)
1703
1754
  end
1704
1755
 
1756
+ def create_index_definition(table_name, name, unique, columns, **options)
1757
+ IndexDefinition.new(table_name, name, unique, columns, **options)
1758
+ end
1759
+
1705
1760
  def create_alter_table(name)
1706
1761
  AlterTable.new create_table_definition(name)
1707
1762
  end
@@ -1764,7 +1819,20 @@ module ActiveRecord
1764
1819
 
1765
1820
  def foreign_key_for(from_table, **options)
1766
1821
  return unless use_foreign_keys?
1767
- 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) }
1768
1836
  end
1769
1837
 
1770
1838
  def foreign_key_for!(from_table, to_table: nil, **options)
@@ -1807,13 +1875,19 @@ module ActiveRecord
1807
1875
 
1808
1876
  def validate_index_length!(table_name, new_name, internal = false)
1809
1877
  if new_name.length > index_name_length
1810
- 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
1811
1882
  end
1812
1883
  end
1813
1884
 
1814
1885
  def validate_table_length!(table_name)
1815
1886
  if table_name.length > table_name_length
1816
- 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
1817
1891
  end
1818
1892
  end
1819
1893
 
@@ -1875,16 +1949,8 @@ module ActiveRecord
1875
1949
  end
1876
1950
 
1877
1951
  def insert_versions_sql(versions)
1878
- sm_table = quote_table_name(pool.schema_migration.table_name)
1879
-
1880
- if versions.is_a?(Array)
1881
- sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1882
- sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1883
- sql << ";"
1884
- sql
1885
- else
1886
- "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1887
- end
1952
+ versions_formatter = ActiveRecord.schema_versions_formatter.new(self)
1953
+ versions_formatter.format(versions)
1888
1954
  end
1889
1955
 
1890
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
@@ -123,6 +124,7 @@ module ActiveRecord
123
124
  def after_commit; yield; end
124
125
  def after_rollback; end
125
126
  def user_transaction; ActiveRecord::Transaction::NULL_TRANSACTION; end
127
+ def isolation=(_); end
126
128
  end
127
129
 
128
130
  class Transaction # :nodoc:
@@ -150,6 +152,15 @@ module ActiveRecord
150
152
 
151
153
  delegate :invalidate!, :invalidated?, to: :@state
152
154
 
155
+ # Returns the isolation level if it was explicitly set, nil otherwise
156
+ def isolation
157
+ @isolation_level
158
+ end
159
+
160
+ def isolation=(isolation) # :nodoc:
161
+ @isolation_level = isolation
162
+ end
163
+
153
164
  def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
154
165
  super()
155
166
  @connection = connection
@@ -175,11 +186,11 @@ module ActiveRecord
175
186
  end
176
187
 
177
188
  def open?
178
- true
189
+ !closed?
179
190
  end
180
191
 
181
192
  def closed?
182
- false
193
+ @state.finalized?
183
194
  end
184
195
 
185
196
  def add_record(record, ensure_finalize = true)
@@ -386,7 +397,7 @@ module ActiveRecord
386
397
  @parent.state.add_child(@state)
387
398
  end
388
399
 
389
- delegate :materialize!, :materialized?, :restart, to: :@parent
400
+ delegate :materialize!, :materialized?, :restart, :isolation, to: :@parent
390
401
 
391
402
  def rollback
392
403
  @state.rollback!
@@ -405,6 +416,7 @@ module ActiveRecord
405
416
  def initialize(connection, savepoint_name, parent_transaction, **options)
406
417
  super(connection, **options)
407
418
 
419
+ @parent_transaction = parent_transaction
408
420
  parent_transaction.state.add_child(@state)
409
421
 
410
422
  if isolation_level
@@ -414,6 +426,15 @@ module ActiveRecord
414
426
  @savepoint_name = savepoint_name
415
427
  end
416
428
 
429
+ # Delegates to parent transaction's isolation level
430
+ def isolation
431
+ @parent_transaction.isolation
432
+ end
433
+
434
+ def isolation=(isolation) # :nodoc:
435
+ @parent_transaction.isolation = isolation
436
+ end
437
+
417
438
  def materialize!
418
439
  connection.create_savepoint(savepoint_name)
419
440
  super
@@ -620,6 +641,7 @@ module ActiveRecord
620
641
  end
621
642
 
622
643
  def within_new_transaction(isolation: nil, joinable: true)
644
+ isolation ||= @connection.pool.pool_transaction_isolation_level
623
645
  @connection.lock.synchronize do
624
646
  transaction = begin_transaction(isolation: isolation, joinable: joinable)
625
647
  begin