activerecord 7.0.8.7 → 7.1.5.1

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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1795 -1424
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +20 -4
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +19 -13
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  21. data/lib/active_record/associations/join_dependency.rb +10 -10
  22. data/lib/active_record/associations/preloader/association.rb +31 -7
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +319 -217
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +21 -8
  33. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  35. data/lib/active_record/attribute_methods/write.rb +6 -6
  36. data/lib/active_record/attribute_methods.rb +145 -21
  37. data/lib/active_record/attributes.rb +3 -3
  38. data/lib/active_record/autosave_association.rb +59 -10
  39. data/lib/active_record/base.rb +7 -2
  40. data/lib/active_record/callbacks.rb +10 -24
  41. data/lib/active_record/coders/column_serializer.rb +61 -0
  42. data/lib/active_record/coders/json.rb +1 -1
  43. data/lib/active_record/coders/yaml_column.rb +70 -42
  44. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  46. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  52. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +296 -127
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -92
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +244 -121
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
  73. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +364 -61
  83. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  84. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  85. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  86. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  87. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  88. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  89. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  90. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +211 -81
  91. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  92. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  93. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  94. data/lib/active_record/connection_adapters.rb +3 -1
  95. data/lib/active_record/connection_handling.rb +72 -95
  96. data/lib/active_record/core.rb +181 -154
  97. data/lib/active_record/counter_cache.rb +52 -27
  98. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  99. data/lib/active_record/database_configurations/database_config.rb +9 -3
  100. data/lib/active_record/database_configurations/hash_config.rb +28 -14
  101. data/lib/active_record/database_configurations/url_config.rb +17 -11
  102. data/lib/active_record/database_configurations.rb +86 -33
  103. data/lib/active_record/delegated_type.rb +15 -10
  104. data/lib/active_record/deprecator.rb +7 -0
  105. data/lib/active_record/destroy_association_async_job.rb +3 -1
  106. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  107. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  108. data/lib/active_record/encryption/config.rb +25 -1
  109. data/lib/active_record/encryption/configurable.rb +12 -19
  110. data/lib/active_record/encryption/context.rb +10 -3
  111. data/lib/active_record/encryption/contexts.rb +5 -1
  112. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  113. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  114. data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
  115. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  116. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  117. data/lib/active_record/encryption/key_generator.rb +12 -1
  118. data/lib/active_record/encryption/message_serializer.rb +2 -0
  119. data/lib/active_record/encryption/properties.rb +3 -3
  120. data/lib/active_record/encryption/scheme.rb +22 -21
  121. data/lib/active_record/encryption.rb +3 -0
  122. data/lib/active_record/enum.rb +112 -28
  123. data/lib/active_record/errors.rb +112 -18
  124. data/lib/active_record/explain.rb +23 -3
  125. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  126. data/lib/active_record/fixture_set/render_context.rb +2 -0
  127. data/lib/active_record/fixture_set/table_row.rb +29 -8
  128. data/lib/active_record/fixtures.rb +135 -71
  129. data/lib/active_record/future_result.rb +40 -5
  130. data/lib/active_record/gem_version.rb +4 -4
  131. data/lib/active_record/inheritance.rb +30 -16
  132. data/lib/active_record/insert_all.rb +57 -10
  133. data/lib/active_record/integration.rb +8 -8
  134. data/lib/active_record/internal_metadata.rb +120 -30
  135. data/lib/active_record/locking/optimistic.rb +1 -1
  136. data/lib/active_record/locking/pessimistic.rb +5 -2
  137. data/lib/active_record/log_subscriber.rb +29 -12
  138. data/lib/active_record/marshalling.rb +59 -0
  139. data/lib/active_record/message_pack.rb +124 -0
  140. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  141. data/lib/active_record/middleware/database_selector.rb +6 -8
  142. data/lib/active_record/middleware/shard_selector.rb +3 -1
  143. data/lib/active_record/migration/command_recorder.rb +104 -5
  144. data/lib/active_record/migration/compatibility.rb +145 -5
  145. data/lib/active_record/migration/default_strategy.rb +23 -0
  146. data/lib/active_record/migration/execution_strategy.rb +19 -0
  147. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  148. data/lib/active_record/migration.rb +219 -111
  149. data/lib/active_record/model_schema.rb +69 -44
  150. data/lib/active_record/nested_attributes.rb +37 -8
  151. data/lib/active_record/normalization.rb +167 -0
  152. data/lib/active_record/persistence.rb +188 -37
  153. data/lib/active_record/promise.rb +84 -0
  154. data/lib/active_record/query_cache.rb +4 -22
  155. data/lib/active_record/query_logs.rb +77 -52
  156. data/lib/active_record/query_logs_formatter.rb +41 -0
  157. data/lib/active_record/querying.rb +15 -2
  158. data/lib/active_record/railtie.rb +107 -45
  159. data/lib/active_record/railties/controller_runtime.rb +12 -6
  160. data/lib/active_record/railties/databases.rake +144 -150
  161. data/lib/active_record/railties/job_runtime.rb +23 -0
  162. data/lib/active_record/readonly_attributes.rb +32 -5
  163. data/lib/active_record/reflection.rb +181 -45
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  165. data/lib/active_record/relation/batches.rb +190 -61
  166. data/lib/active_record/relation/calculations.rb +187 -63
  167. data/lib/active_record/relation/delegation.rb +23 -9
  168. data/lib/active_record/relation/finder_methods.rb +77 -16
  169. data/lib/active_record/relation/merger.rb +2 -0
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  173. data/lib/active_record/relation/predicate_builder.rb +26 -14
  174. data/lib/active_record/relation/query_attribute.rb +2 -1
  175. data/lib/active_record/relation/query_methods.rb +371 -68
  176. data/lib/active_record/relation/spawn_methods.rb +18 -1
  177. data/lib/active_record/relation.rb +103 -37
  178. data/lib/active_record/result.rb +19 -5
  179. data/lib/active_record/runtime_registry.rb +24 -1
  180. data/lib/active_record/sanitization.rb +51 -11
  181. data/lib/active_record/schema.rb +2 -3
  182. data/lib/active_record/schema_dumper.rb +46 -7
  183. data/lib/active_record/schema_migration.rb +68 -33
  184. data/lib/active_record/scoping/default.rb +15 -5
  185. data/lib/active_record/scoping/named.rb +2 -2
  186. data/lib/active_record/scoping.rb +2 -1
  187. data/lib/active_record/secure_password.rb +60 -0
  188. data/lib/active_record/secure_token.rb +21 -3
  189. data/lib/active_record/signed_id.rb +7 -5
  190. data/lib/active_record/store.rb +8 -8
  191. data/lib/active_record/suppressor.rb +3 -1
  192. data/lib/active_record/table_metadata.rb +10 -1
  193. data/lib/active_record/tasks/database_tasks.rb +152 -108
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  196. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  197. data/lib/active_record/test_fixtures.rb +114 -96
  198. data/lib/active_record/timestamp.rb +30 -16
  199. data/lib/active_record/token_for.rb +113 -0
  200. data/lib/active_record/touch_later.rb +11 -6
  201. data/lib/active_record/transactions.rb +36 -10
  202. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  203. data/lib/active_record/type/internal/timezone.rb +7 -2
  204. data/lib/active_record/type/time.rb +4 -0
  205. data/lib/active_record/validations/absence.rb +1 -1
  206. data/lib/active_record/validations/numericality.rb +5 -4
  207. data/lib/active_record/validations/presence.rb +5 -28
  208. data/lib/active_record/validations/uniqueness.rb +47 -2
  209. data/lib/active_record/validations.rb +8 -4
  210. data/lib/active_record/version.rb +1 -1
  211. data/lib/active_record.rb +122 -17
  212. data/lib/arel/errors.rb +10 -0
  213. data/lib/arel/factory_methods.rb +4 -0
  214. data/lib/arel/nodes/binary.rb +6 -1
  215. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  216. data/lib/arel/nodes/cte.rb +36 -0
  217. data/lib/arel/nodes/fragments.rb +35 -0
  218. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  219. data/lib/arel/nodes/leading_join.rb +8 -0
  220. data/lib/arel/nodes/node.rb +111 -2
  221. data/lib/arel/nodes/sql_literal.rb +6 -0
  222. data/lib/arel/nodes/table_alias.rb +4 -0
  223. data/lib/arel/nodes.rb +4 -0
  224. data/lib/arel/predications.rb +2 -0
  225. data/lib/arel/table.rb +9 -5
  226. data/lib/arel/tree_manager.rb +5 -1
  227. data/lib/arel/visitors/mysql.rb +8 -1
  228. data/lib/arel/visitors/to_sql.rb +83 -18
  229. data/lib/arel/visitors/visitor.rb +2 -2
  230. data/lib/arel.rb +16 -2
  231. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  232. data/lib/rails/generators/active_record/migration.rb +3 -1
  233. data/lib/rails/generators/active_record/model/USAGE +113 -0
  234. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  235. metadata +46 -10
  236. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  237. data/lib/active_record/null_relation.rb +0 -63
@@ -96,26 +96,19 @@ module ActiveRecord
96
96
  # # Check an index with a custom name exists
97
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
98
98
  #
99
+ # # Check a valid index exists (PostgreSQL only)
100
+ # index_exists?(:suppliers, :company_id, valid: true)
101
+ #
99
102
  def index_exists?(table_name, column_name, **options)
100
- checks = []
101
- column_name = options[:column] if column_name.nil?
102
-
103
- if column_name.present?
104
- column_names = Array(column_name).map(&:to_s)
105
- checks << lambda { |i| Array(i.columns) == column_names }
106
- end
107
-
108
- checks << lambda { |i| i.unique } if options[:unique]
109
- checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
110
-
111
- indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
103
+ indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
112
104
  end
113
105
 
114
106
  # Returns an array of +Column+ objects for the table specified by +table_name+.
115
107
  def columns(table_name)
116
108
  table_name = table_name.to_s
117
- column_definitions(table_name).map do |field|
118
- new_column_from_field(table_name, field)
109
+ definitions = column_definitions(table_name)
110
+ definitions.map do |field|
111
+ new_column_from_field(table_name, field, definitions)
119
112
  end
120
113
  end
121
114
 
@@ -297,25 +290,10 @@ module ActiveRecord
297
290
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
298
291
  #
299
292
  # See also TableDefinition#column for details on how to create columns.
300
- def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
301
- td = create_table_definition(table_name, **extract_table_options!(options))
302
-
303
- if id && !td.as
304
- pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
305
-
306
- if id.is_a?(Hash)
307
- options.merge!(id.except(:type))
308
- id = id.fetch(:type, :primary_key)
309
- end
310
-
311
- if pk.is_a?(Array)
312
- td.primary_keys pk
313
- else
314
- td.primary_key pk, id, **options
315
- end
316
- end
317
-
318
- yield td if block_given?
293
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
294
+ validate_create_table_options!(options)
295
+ validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
296
+ td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
319
297
 
320
298
  if force
321
299
  drop_table(table_name, force: force, if_exists: true)
@@ -323,7 +301,7 @@ module ActiveRecord
323
301
  schema_cache.clear_data_source_cache!(table_name.to_s)
324
302
  end
325
303
 
326
- result = execute schema_creation.accept td
304
+ result = execute schema_creation.accept(td)
327
305
 
328
306
  unless supports_indexes_in_create?
329
307
  td.indexes.each do |column_name, index_options|
@@ -344,6 +322,18 @@ module ActiveRecord
344
322
  result
345
323
  end
346
324
 
325
+ # Returns a TableDefinition object containing information about the table that would be created
326
+ # if the same arguments were passed to #create_table. See #create_table for information about
327
+ # passing a +table_name+, and other additional options that can be passed.
328
+ def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
329
+ table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
330
+ table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
331
+
332
+ yield table_definition if block_given?
333
+
334
+ table_definition
335
+ end
336
+
347
337
  # Creates a new join table with the name created using the lexical order of the first two
348
338
  # arguments. These arguments can be a String or a Symbol.
349
339
  #
@@ -387,7 +377,7 @@ module ActiveRecord
387
377
 
388
378
  column_options.reverse_merge!(null: false, index: false)
389
379
 
390
- t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
380
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
391
381
 
392
382
  create_table(join_table_name, **options.merge!(id: false)) do |td|
393
383
  td.references t1_ref, **column_options
@@ -396,15 +386,33 @@ module ActiveRecord
396
386
  end
397
387
  end
398
388
 
389
+ # Builds a TableDefinition object for a join table.
390
+ #
391
+ # This definition object contains information about the table that would be created
392
+ # if the same arguments were passed to #create_join_table. See #create_join_table for
393
+ # information about what arguments should be passed.
394
+ def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
395
+ join_table_name = find_join_table_name(table_1, table_2, options)
396
+ column_options.reverse_merge!(null: false, index: false)
397
+
398
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
399
+
400
+ build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
401
+ td.references t1_ref, **column_options
402
+ td.references t2_ref, **column_options
403
+ yield td if block_given?
404
+ end
405
+ end
406
+
399
407
  # Drops the join table specified by the given arguments.
400
- # See #create_join_table for details.
408
+ # See #create_join_table and #drop_table for details.
401
409
  #
402
410
  # Although this command ignores the block if one is given, it can be helpful
403
411
  # to provide one in a migration's +change+ method so it can be reverted.
404
412
  # In that case, the block will be used by #create_join_table.
405
413
  def drop_join_table(table_1, table_2, **options)
406
414
  join_table_name = find_join_table_name(table_1, table_2, options)
407
- drop_table(join_table_name)
415
+ drop_table(join_table_name, **options)
408
416
  end
409
417
 
410
418
  # A block for changing columns in +table+.
@@ -485,13 +493,13 @@ module ActiveRecord
485
493
  # end
486
494
  #
487
495
  # See also Table for details on all of the various column transformations.
488
- def change_table(table_name, **options)
496
+ def change_table(table_name, base = self, **options)
489
497
  if supports_bulk_alter? && options[:bulk]
490
498
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
491
499
  yield update_table_definition(table_name, recorder)
492
500
  bulk_change_table(table_name, recorder.commands)
493
501
  else
494
- yield update_table_definition(table_name, self)
502
+ yield update_table_definition(table_name, base)
495
503
  end
496
504
  end
497
505
 
@@ -499,7 +507,7 @@ module ActiveRecord
499
507
  #
500
508
  # rename_table('octopuses', 'octopi')
501
509
  #
502
- def rename_table(table_name, new_name)
510
+ def rename_table(table_name, new_name, **)
503
511
  raise NotImplementedError, "rename_table is not implemented"
504
512
  end
505
513
 
@@ -554,11 +562,6 @@ module ActiveRecord
554
562
  # <tt>:datetime</tt>, and <tt>:time</tt> columns.
555
563
  # * <tt>:scale</tt> -
556
564
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
557
- # * <tt>:collation</tt> -
558
- # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
559
- # column will have the same collation as the table.
560
- # * <tt>:comment</tt> -
561
- # Specifies the comment for the column. This option is ignored by some backends.
562
565
  # * <tt>:if_not_exists</tt> -
563
566
  # Specifies if the column already exists to not try to re-add it. This will avoid
564
567
  # duplicate column errors.
@@ -574,7 +577,7 @@ module ActiveRecord
574
577
  # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
575
578
  # <tt>:precision</tt>, and makes no comments about the requirements of
576
579
  # <tt>:precision</tt>.
577
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
580
+ # * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
578
581
  # Default is (10,0).
579
582
  # * PostgreSQL: <tt>:precision</tt> [1..infinity],
580
583
  # <tt>:scale</tt> [0..infinity]. No default.
@@ -615,6 +618,24 @@ module ActiveRecord
615
618
  # # Ignores the method call if the column exists
616
619
  # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
617
620
  def add_column(table_name, column_name, type, **options)
621
+ add_column_def = build_add_column_definition(table_name, column_name, type, **options)
622
+ return unless add_column_def
623
+
624
+ execute schema_creation.accept(add_column_def)
625
+ end
626
+
627
+ def add_columns(table_name, *column_names, type:, **options) # :nodoc:
628
+ column_names.each do |column_name|
629
+ add_column(table_name, column_name, type, **options)
630
+ end
631
+ end
632
+
633
+ # Builds an AlterTable object for adding a column to a table.
634
+ #
635
+ # This definition object contains information about the column that would be created
636
+ # if the same arguments were passed to #add_column. See #add_column for information about
637
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
638
+ def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
618
639
  return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
619
640
 
620
641
  if supports_datetime_with_precision?
@@ -623,15 +644,9 @@ module ActiveRecord
623
644
  end
624
645
  end
625
646
 
626
- at = create_alter_table table_name
627
- at.add_column(column_name, type, **options)
628
- execute schema_creation.accept at
629
- end
630
-
631
- def add_columns(table_name, *column_names, type:, **options) # :nodoc:
632
- column_names.each do |column_name|
633
- add_column(table_name, column_name, type, **options)
634
- end
647
+ alter_table = create_alter_table(table_name)
648
+ alter_table.add_column(column_name, type, **options)
649
+ alter_table
635
650
  end
636
651
 
637
652
  # Removes the given columns from the table definition.
@@ -699,6 +714,15 @@ module ActiveRecord
699
714
  raise NotImplementedError, "change_column_default is not implemented"
700
715
  end
701
716
 
717
+ # Builds a ChangeColumnDefaultDefinition object.
718
+ #
719
+ # This definition object contains information about the column change that would occur
720
+ # if the same arguments were passed to #change_column_default. See #change_column_default for
721
+ # information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
722
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
723
+ raise NotImplementedError, "build_change_column_default_definition is not implemented"
724
+ end
725
+
702
726
  # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
703
727
  # indicates whether the value can be +NULL+. For example
704
728
  #
@@ -805,6 +829,16 @@ module ActiveRecord
805
829
  #
806
830
  # Note: Partial indexes are only supported for PostgreSQL and SQLite.
807
831
  #
832
+ # ====== Creating an index that includes additional columns
833
+ #
834
+ # add_index(:accounts, :branch_id, include: :party_id)
835
+ #
836
+ # generates:
837
+ #
838
+ # CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
839
+ #
840
+ # Note: only supported by PostgreSQL.
841
+ #
808
842
  # ====== Creating an index with a specific method
809
843
  #
810
844
  # add_index(:developers, :name, using: 'btree')
@@ -850,12 +884,20 @@ module ActiveRecord
850
884
  #
851
885
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
852
886
  def add_index(table_name, column_name, **options)
853
- index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
854
-
855
- create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
887
+ create_index = build_create_index_definition(table_name, column_name, **options)
856
888
  execute schema_creation.accept(create_index)
857
889
  end
858
890
 
891
+ # Builds a CreateIndexDefinition object.
892
+ #
893
+ # This definition object contains information about the index that would be created
894
+ # if the same arguments were passed to #add_index. See #add_index for information about
895
+ # passing a +table_name+, +column_name+, and other additional options that can be passed.
896
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
897
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
898
+ CreateIndexDefinition.new(index, algorithm, if_not_exists)
899
+ end
900
+
859
901
  # Removes the given index from the table.
860
902
  #
861
903
  # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
@@ -921,7 +963,11 @@ module ActiveRecord
921
963
  def index_name(table_name, options) # :nodoc:
922
964
  if Hash === options
923
965
  if options[:column]
924
- "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
966
+ if options[:_uses_legacy_index_name]
967
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
968
+ else
969
+ generate_index_name(table_name, options[:column])
970
+ end
925
971
  elsif options[:name]
926
972
  options[:name]
927
973
  else
@@ -941,7 +987,6 @@ module ActiveRecord
941
987
  # Adds a reference. The reference column is a bigint by default,
942
988
  # the <tt>:type</tt> option can be used to specify a different type.
943
989
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
944
- # #add_reference and #add_belongs_to are acceptable.
945
990
  #
946
991
  # The +options+ hash can include the following keys:
947
992
  # [<tt>:type</tt>]
@@ -987,12 +1032,11 @@ module ActiveRecord
987
1032
  # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
988
1033
  #
989
1034
  def add_reference(table_name, ref_name, **options)
990
- ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
1035
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
991
1036
  end
992
1037
  alias :add_belongs_to :add_reference
993
1038
 
994
1039
  # Removes the reference(s). Also removes a +type+ column if one exists.
995
- # #remove_reference and #remove_belongs_to are acceptable.
996
1040
  #
997
1041
  # ====== Remove the reference
998
1042
  #
@@ -1007,19 +1051,21 @@ module ActiveRecord
1007
1051
  # remove_reference(:products, :user, foreign_key: true)
1008
1052
  #
1009
1053
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1054
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1055
+
1010
1056
  if foreign_key
1011
1057
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
1012
1058
  if foreign_key.is_a?(Hash)
1013
- foreign_key_options = foreign_key
1059
+ foreign_key_options = foreign_key.merge(conditional_options)
1014
1060
  else
1015
- foreign_key_options = { to_table: reference_name }
1061
+ foreign_key_options = { to_table: reference_name, **conditional_options }
1016
1062
  end
1017
1063
  foreign_key_options[:column] ||= "#{ref_name}_id"
1018
1064
  remove_foreign_key(table_name, **foreign_key_options)
1019
1065
  end
1020
1066
 
1021
- remove_column(table_name, "#{ref_name}_id")
1022
- remove_column(table_name, "#{ref_name}_type") if polymorphic
1067
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1068
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
1023
1069
  end
1024
1070
  alias :remove_belongs_to :remove_reference
1025
1071
 
@@ -1056,6 +1102,16 @@ module ActiveRecord
1056
1102
  #
1057
1103
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
1058
1104
  #
1105
+ # ====== Creating a composite foreign key
1106
+ #
1107
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1108
+ #
1109
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1110
+ #
1111
+ # generates:
1112
+ #
1113
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1114
+ #
1059
1115
  # ====== Creating a cascading foreign key
1060
1116
  #
1061
1117
  # add_foreign_key :articles, :authors, on_delete: :cascade
@@ -1066,9 +1122,11 @@ module ActiveRecord
1066
1122
  #
1067
1123
  # The +options+ hash can include the following keys:
1068
1124
  # [<tt>:column</tt>]
1069
- # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
1125
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1126
+ # Pass an array to create a composite foreign key.
1070
1127
  # [<tt>:primary_key</tt>]
1071
1128
  # The primary key column name on +to_table+. Defaults to +id+.
1129
+ # Pass an array to create a composite foreign key.
1072
1130
  # [<tt>:name</tt>]
1073
1131
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
1074
1132
  # [<tt>:on_delete</tt>]
@@ -1084,8 +1142,8 @@ module ActiveRecord
1084
1142
  # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1085
1143
  # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1086
1144
  def add_foreign_key(from_table, to_table, **options)
1087
- return unless supports_foreign_keys?
1088
- return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table)
1145
+ return unless use_foreign_keys?
1146
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1089
1147
 
1090
1148
  options = foreign_key_options(from_table, to_table, options)
1091
1149
  at = create_alter_table from_table
@@ -1125,7 +1183,7 @@ module ActiveRecord
1125
1183
  # [<tt>:to_table</tt>]
1126
1184
  # The name of the table that contains the referenced primary key.
1127
1185
  def remove_foreign_key(from_table, to_table = nil, **options)
1128
- return unless supports_foreign_keys?
1186
+ return unless use_foreign_keys?
1129
1187
  return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1130
1188
 
1131
1189
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
@@ -1151,15 +1209,33 @@ module ActiveRecord
1151
1209
  foreign_key_for(from_table, to_table: to_table, **options).present?
1152
1210
  end
1153
1211
 
1154
- def foreign_key_column_for(table_name) # :nodoc:
1212
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1155
1213
  name = strip_table_name_prefix_and_suffix(table_name)
1156
- "#{name.singularize}_id"
1214
+ "#{name.singularize}_#{column_name}"
1157
1215
  end
1158
1216
 
1159
1217
  def foreign_key_options(from_table, to_table, options) # :nodoc:
1160
1218
  options = options.dup
1161
- options[:column] ||= foreign_key_column_for(to_table)
1219
+
1220
+ if options[:primary_key].is_a?(Array)
1221
+ options[:column] ||= options[:primary_key].map do |pk_column|
1222
+ foreign_key_column_for(to_table, pk_column)
1223
+ end
1224
+ else
1225
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1226
+ end
1227
+
1162
1228
  options[:name] ||= foreign_key_name(from_table, options)
1229
+
1230
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1231
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1232
+ raise ArgumentError, <<~MSG.squish
1233
+ For composite primary keys, specify :column and :primary_key, where
1234
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1235
+ MSG
1236
+ end
1237
+ end
1238
+
1163
1239
  options
1164
1240
  end
1165
1241
 
@@ -1181,12 +1257,16 @@ module ActiveRecord
1181
1257
  # The +options+ hash can include the following keys:
1182
1258
  # [<tt>:name</tt>]
1183
1259
  # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1260
+ # [<tt>:if_not_exists</tt>]
1261
+ # Silently ignore if the constraint already exists, rather than raise an error.
1184
1262
  # [<tt>:validate</tt>]
1185
1263
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1186
- def add_check_constraint(table_name, expression, **options)
1264
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1187
1265
  return unless supports_check_constraints?
1188
1266
 
1189
1267
  options = check_constraint_options(table_name, expression, options)
1268
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1269
+
1190
1270
  at = create_alter_table(table_name)
1191
1271
  at.add_check_constraint(expression, options)
1192
1272
 
@@ -1199,16 +1279,24 @@ module ActiveRecord
1199
1279
  options
1200
1280
  end
1201
1281
 
1202
- # Removes the given check constraint from the table.
1282
+ # Removes the given check constraint from the table. Removing a check constraint
1283
+ # that does not exist will raise an error.
1203
1284
  #
1204
1285
  # remove_check_constraint :products, name: "price_check"
1205
1286
  #
1287
+ # To silently ignore a non-existent check constraint rather than raise an error,
1288
+ # use the +if_exists+ option.
1289
+ #
1290
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1291
+ #
1206
1292
  # The +expression+ parameter will be ignored if present. It can be helpful
1207
1293
  # to provide this in a migration's +change+ method so it can be reverted.
1208
1294
  # In that case, +expression+ will be used by #add_check_constraint.
1209
- def remove_check_constraint(table_name, expression = nil, **options)
1295
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1210
1296
  return unless supports_check_constraints?
1211
1297
 
1298
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1299
+
1212
1300
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1213
1301
 
1214
1302
  at = create_alter_table(table_name)
@@ -1217,8 +1305,20 @@ module ActiveRecord
1217
1305
  execute schema_creation.accept(at)
1218
1306
  end
1219
1307
 
1308
+
1309
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1310
+ #
1311
+ # check_constraint_exists?(:products, name: "price_check")
1312
+ #
1313
+ def check_constraint_exists?(table_name, **options)
1314
+ if !options.key?(:name) && !options.key?(:expression)
1315
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1316
+ end
1317
+ check_constraint_for(table_name, **options).present?
1318
+ end
1319
+
1220
1320
  def dump_schema_information # :nodoc:
1221
- versions = schema_migration.all_versions
1321
+ versions = schema_migration.versions
1222
1322
  insert_versions_sql(versions) if versions.any?
1223
1323
  end
1224
1324
 
@@ -1291,18 +1391,24 @@ module ActiveRecord
1291
1391
  end
1292
1392
 
1293
1393
  def distinct_relation_for_primary_key(relation) # :nodoc:
1394
+ primary_key_columns = Array(relation.primary_key).map do |column|
1395
+ visitor.compile(relation.table[column])
1396
+ end
1397
+
1294
1398
  values = columns_for_distinct(
1295
- visitor.compile(relation.table[relation.primary_key]),
1399
+ primary_key_columns,
1296
1400
  relation.order_values
1297
1401
  )
1298
1402
 
1299
1403
  limited = relation.reselect(values).distinct!
1300
- limited_ids = select_rows(limited.arel, "SQL").map(&:last)
1404
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1405
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
1406
+ end
1301
1407
 
1302
1408
  if limited_ids.empty?
1303
1409
  relation.none!
1304
1410
  else
1305
- relation.where!(relation.primary_key => limited_ids)
1411
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1306
1412
  end
1307
1413
 
1308
1414
  relation.limit_value = relation.offset_value = nil
@@ -1315,14 +1421,8 @@ module ActiveRecord
1315
1421
  # add_timestamps(:suppliers, null: true)
1316
1422
  #
1317
1423
  def add_timestamps(table_name, **options)
1318
- options[:null] = false if options[:null].nil?
1319
-
1320
- if !options.key?(:precision) && supports_datetime_with_precision?
1321
- options[:precision] = 6
1322
- end
1323
-
1324
- add_column table_name, :created_at, :datetime, **options
1325
- add_column table_name, :updated_at, :datetime, **options
1424
+ fragments = add_timestamps_for_alter(table_name, **options)
1425
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1326
1426
  end
1327
1427
 
1328
1428
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
@@ -1338,7 +1438,7 @@ module ActiveRecord
1338
1438
  end
1339
1439
 
1340
1440
  def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1341
- options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
1441
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1342
1442
 
1343
1443
  column_names = index_column_names(column_name)
1344
1444
 
@@ -1357,6 +1457,8 @@ module ActiveRecord
1357
1457
  where: options[:where],
1358
1458
  type: options[:type],
1359
1459
  using: options[:using],
1460
+ include: options[:include],
1461
+ nulls_not_distinct: options[:nulls_not_distinct],
1360
1462
  comment: options[:comment]
1361
1463
  )
1362
1464
 
@@ -1404,7 +1506,79 @@ module ActiveRecord
1404
1506
  SchemaDumper.create(self, options)
1405
1507
  end
1406
1508
 
1509
+ def use_foreign_keys?
1510
+ supports_foreign_keys? && foreign_keys_enabled?
1511
+ end
1512
+
1513
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1514
+ # object and return DDL.
1515
+ def schema_creation # :nodoc:
1516
+ SchemaCreation.new(self)
1517
+ end
1518
+
1519
+ def bulk_change_table(table_name, operations) # :nodoc:
1520
+ sql_fragments = []
1521
+ non_combinable_operations = []
1522
+
1523
+ operations.each do |command, args|
1524
+ table, arguments = args.shift, args
1525
+ method = :"#{command}_for_alter"
1526
+
1527
+ if respond_to?(method, true)
1528
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1529
+ sql_fragments.concat(sqls)
1530
+ non_combinable_operations.concat(procs)
1531
+ else
1532
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1533
+ non_combinable_operations.each(&:call)
1534
+ sql_fragments = []
1535
+ non_combinable_operations = []
1536
+ send(command, table, *arguments)
1537
+ end
1538
+ end
1539
+
1540
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1541
+ non_combinable_operations.each(&:call)
1542
+ end
1543
+
1544
+ def valid_table_definition_options # :nodoc:
1545
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1546
+ end
1547
+
1548
+ def valid_column_definition_options # :nodoc:
1549
+ ColumnDefinition::OPTION_NAMES
1550
+ end
1551
+
1552
+ def valid_primary_key_options # :nodoc:
1553
+ [:limit, :default, :precision]
1554
+ end
1555
+
1556
+ # Returns the maximum length of an index name in bytes.
1557
+ def max_index_name_size
1558
+ 62
1559
+ end
1560
+
1407
1561
  private
1562
+ def generate_index_name(table_name, column)
1563
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1564
+ return name if name.bytesize <= max_index_name_size
1565
+
1566
+ # Fallback to short version, add hash to ensure uniqueness
1567
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1568
+ name = "idx_on_#{Array(column) * '_'}"
1569
+
1570
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1571
+ short_name = name.mb_chars.limit(short_limit).to_s
1572
+
1573
+ "#{short_name}#{hashed_identifier}"
1574
+ end
1575
+
1576
+ def validate_change_column_null_argument!(value)
1577
+ unless value == true || value == false
1578
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1579
+ end
1580
+ end
1581
+
1408
1582
  def column_options_keys
1409
1583
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
1410
1584
  end
@@ -1458,7 +1632,7 @@ module ActiveRecord
1458
1632
 
1459
1633
  if matching_indexes.count > 1
1460
1634
  raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1461
- "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1635
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1462
1636
  elsif matching_indexes.none?
1463
1637
  raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1464
1638
  else
@@ -1466,11 +1640,11 @@ module ActiveRecord
1466
1640
  end
1467
1641
  end
1468
1642
 
1469
- def rename_table_indexes(table_name, new_name)
1643
+ def rename_table_indexes(table_name, new_name, **options)
1470
1644
  indexes(new_name).each do |index|
1471
- generated_index_name = index_name(table_name, column: index.columns)
1645
+ generated_index_name = index_name(table_name, column: index.columns, **options)
1472
1646
  if generated_index_name == index.name
1473
- rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
1647
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
1474
1648
  end
1475
1649
  end
1476
1650
  end
@@ -1488,10 +1662,6 @@ module ActiveRecord
1488
1662
  end
1489
1663
  end
1490
1664
 
1491
- def schema_creation
1492
- SchemaCreation.new(self)
1493
- end
1494
-
1495
1665
  def create_table_definition(name, **options)
1496
1666
  TableDefinition.new(self, name, **options)
1497
1667
  end
@@ -1500,8 +1670,12 @@ module ActiveRecord
1500
1670
  AlterTable.new create_table_definition(name)
1501
1671
  end
1502
1672
 
1503
- def extract_table_options!(options)
1504
- options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1673
+ def validate_create_table_options!(options)
1674
+ unless options[:_skip_validate_options]
1675
+ options
1676
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1677
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1678
+ end
1505
1679
  end
1506
1680
 
1507
1681
  def fetch_type_metadata(sql_type)
@@ -1544,7 +1718,8 @@ module ActiveRecord
1544
1718
 
1545
1719
  def foreign_key_name(table_name, options)
1546
1720
  options.fetch(:name) do
1547
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1721
+ columns = Array(options.fetch(:column)).map(&:to_s)
1722
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1548
1723
  hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1549
1724
 
1550
1725
  "fk_rails_#{hashed_identifier}"
@@ -1552,7 +1727,7 @@ module ActiveRecord
1552
1727
  end
1553
1728
 
1554
1729
  def foreign_key_for(from_table, **options)
1555
- return unless supports_foreign_keys?
1730
+ return unless use_foreign_keys?
1556
1731
  foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1557
1732
  end
1558
1733
 
@@ -1569,6 +1744,10 @@ module ActiveRecord
1569
1744
  end
1570
1745
  end
1571
1746
 
1747
+ def foreign_keys_enabled?
1748
+ @config.fetch(:foreign_keys, true)
1749
+ end
1750
+
1572
1751
  def check_constraint_name(table_name, **options)
1573
1752
  options.fetch(:name) do
1574
1753
  expression = options.fetch(:expression)
@@ -1582,7 +1761,7 @@ module ActiveRecord
1582
1761
  def check_constraint_for(table_name, **options)
1583
1762
  return unless supports_check_constraints?
1584
1763
  chk_name = check_constraint_name(table_name, **options)
1585
- check_constraints(table_name).detect { |chk| chk.name == chk_name }
1764
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1586
1765
  end
1587
1766
 
1588
1767
  def check_constraint_for!(table_name, expression: nil, **options)
@@ -1596,6 +1775,12 @@ module ActiveRecord
1596
1775
  end
1597
1776
  end
1598
1777
 
1778
+ def validate_table_length!(table_name)
1779
+ if table_name.length > table_name_length
1780
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1781
+ end
1782
+ end
1783
+
1599
1784
  def extract_new_default_value(default_or_changes)
1600
1785
  if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1601
1786
  default_or_changes[:to]
@@ -1609,29 +1794,8 @@ module ActiveRecord
1609
1794
  column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1610
1795
  end
1611
1796
 
1612
- def bulk_change_table(table_name, operations)
1613
- sql_fragments = []
1614
- non_combinable_operations = []
1615
-
1616
- operations.each do |command, args|
1617
- table, arguments = args.shift, args
1618
- method = :"#{command}_for_alter"
1619
-
1620
- if respond_to?(method, true)
1621
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1622
- sql_fragments << sqls
1623
- non_combinable_operations.concat(procs)
1624
- else
1625
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1626
- non_combinable_operations.each(&:call)
1627
- sql_fragments = []
1628
- non_combinable_operations = []
1629
- send(command, table, *arguments)
1630
- end
1631
- end
1632
-
1633
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1634
- non_combinable_operations.each(&:call)
1797
+ def reference_name_for_table(table_name)
1798
+ table_name.to_s.singularize
1635
1799
  end
1636
1800
 
1637
1801
  def add_column_for_alter(table_name, column_name, type, **options)
@@ -1640,6 +1804,11 @@ module ActiveRecord
1640
1804
  schema_creation.accept(AddColumnDefinition.new(cd))
1641
1805
  end
1642
1806
 
1807
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1808
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1809
+ schema_creation.accept(cd)
1810
+ end
1811
+
1643
1812
  def rename_column_sql(table_name, column_name, new_column_name)
1644
1813
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1645
1814
  end
@@ -1674,8 +1843,8 @@ module ActiveRecord
1674
1843
 
1675
1844
  if versions.is_a?(Array)
1676
1845
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1677
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1678
- sql << ";\n\n"
1846
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1847
+ sql << ";"
1679
1848
  sql
1680
1849
  else
1681
1850
  "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"