activerecord 7.0.8.7 → 7.2.3

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 (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +31 -23
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +40 -9
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +35 -21
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +56 -27
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +48 -10
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +236 -118
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +90 -23
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +33 -11
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +108 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +3 -1
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. 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
 
@@ -193,6 +186,9 @@ module ActiveRecord
193
186
  # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
194
187
  #
195
188
  # A Symbol can be used to specify the type of the generated primary key column.
189
+ #
190
+ # A Hash can be used to specify the generated primary key column creation options.
191
+ # See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
196
192
  # [<tt>:primary_key</tt>]
197
193
  # The name of the primary key, if one is to be added automatically.
198
194
  # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
@@ -297,25 +293,15 @@ module ActiveRecord
297
293
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
298
294
  #
299
295
  # 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
296
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
297
+ validate_create_table_options!(options)
298
+ validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
310
299
 
311
- if pk.is_a?(Array)
312
- td.primary_keys pk
313
- else
314
- td.primary_key pk, id, **options
315
- end
300
+ if force && options.key?(:if_not_exists)
301
+ raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
316
302
  end
317
303
 
318
- yield td if block_given?
304
+ td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
319
305
 
320
306
  if force
321
307
  drop_table(table_name, force: force, if_exists: true)
@@ -323,7 +309,7 @@ module ActiveRecord
323
309
  schema_cache.clear_data_source_cache!(table_name.to_s)
324
310
  end
325
311
 
326
- result = execute schema_creation.accept td
312
+ result = execute schema_creation.accept(td)
327
313
 
328
314
  unless supports_indexes_in_create?
329
315
  td.indexes.each do |column_name, index_options|
@@ -344,6 +330,18 @@ module ActiveRecord
344
330
  result
345
331
  end
346
332
 
333
+ # Returns a TableDefinition object containing information about the table that would be created
334
+ # if the same arguments were passed to #create_table. See #create_table for information about
335
+ # passing a +table_name+, and other additional options that can be passed.
336
+ def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
337
+ table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
338
+ table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
339
+
340
+ yield table_definition if block_given?
341
+
342
+ table_definition
343
+ end
344
+
347
345
  # Creates a new join table with the name created using the lexical order of the first two
348
346
  # arguments. These arguments can be a String or a Symbol.
349
347
  #
@@ -387,7 +385,7 @@ module ActiveRecord
387
385
 
388
386
  column_options.reverse_merge!(null: false, index: false)
389
387
 
390
- t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
388
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
391
389
 
392
390
  create_table(join_table_name, **options.merge!(id: false)) do |td|
393
391
  td.references t1_ref, **column_options
@@ -396,15 +394,33 @@ module ActiveRecord
396
394
  end
397
395
  end
398
396
 
397
+ # Builds a TableDefinition object for a join table.
398
+ #
399
+ # This definition object contains information about the table that would be created
400
+ # if the same arguments were passed to #create_join_table. See #create_join_table for
401
+ # information about what arguments should be passed.
402
+ def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
403
+ join_table_name = find_join_table_name(table_1, table_2, options)
404
+ column_options.reverse_merge!(null: false, index: false)
405
+
406
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
407
+
408
+ build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
409
+ td.references t1_ref, **column_options
410
+ td.references t2_ref, **column_options
411
+ yield td if block_given?
412
+ end
413
+ end
414
+
399
415
  # Drops the join table specified by the given arguments.
400
- # See #create_join_table for details.
416
+ # See #create_join_table and #drop_table for details.
401
417
  #
402
418
  # Although this command ignores the block if one is given, it can be helpful
403
419
  # to provide one in a migration's +change+ method so it can be reverted.
404
420
  # In that case, the block will be used by #create_join_table.
405
421
  def drop_join_table(table_1, table_2, **options)
406
422
  join_table_name = find_join_table_name(table_1, table_2, options)
407
- drop_table(join_table_name)
423
+ drop_table(join_table_name, **options)
408
424
  end
409
425
 
410
426
  # A block for changing columns in +table+.
@@ -485,13 +501,13 @@ module ActiveRecord
485
501
  # end
486
502
  #
487
503
  # See also Table for details on all of the various column transformations.
488
- def change_table(table_name, **options)
504
+ def change_table(table_name, base = self, **options)
489
505
  if supports_bulk_alter? && options[:bulk]
490
506
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
491
507
  yield update_table_definition(table_name, recorder)
492
508
  bulk_change_table(table_name, recorder.commands)
493
509
  else
494
- yield update_table_definition(table_name, self)
510
+ yield update_table_definition(table_name, base)
495
511
  end
496
512
  end
497
513
 
@@ -499,7 +515,7 @@ module ActiveRecord
499
515
  #
500
516
  # rename_table('octopuses', 'octopi')
501
517
  #
502
- def rename_table(table_name, new_name)
518
+ def rename_table(table_name, new_name, **)
503
519
  raise NotImplementedError, "rename_table is not implemented"
504
520
  end
505
521
 
@@ -554,11 +570,6 @@ module ActiveRecord
554
570
  # <tt>:datetime</tt>, and <tt>:time</tt> columns.
555
571
  # * <tt>:scale</tt> -
556
572
  # 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
573
  # * <tt>:if_not_exists</tt> -
563
574
  # Specifies if the column already exists to not try to re-add it. This will avoid
564
575
  # duplicate column errors.
@@ -574,7 +585,7 @@ module ActiveRecord
574
585
  # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
575
586
  # <tt>:precision</tt>, and makes no comments about the requirements of
576
587
  # <tt>:precision</tt>.
577
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
588
+ # * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
578
589
  # Default is (10,0).
579
590
  # * PostgreSQL: <tt>:precision</tt> [1..infinity],
580
591
  # <tt>:scale</tt> [0..infinity]. No default.
@@ -615,6 +626,24 @@ module ActiveRecord
615
626
  # # Ignores the method call if the column exists
616
627
  # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
617
628
  def add_column(table_name, column_name, type, **options)
629
+ add_column_def = build_add_column_definition(table_name, column_name, type, **options)
630
+ return unless add_column_def
631
+
632
+ execute schema_creation.accept(add_column_def)
633
+ end
634
+
635
+ def add_columns(table_name, *column_names, type:, **options) # :nodoc:
636
+ column_names.each do |column_name|
637
+ add_column(table_name, column_name, type, **options)
638
+ end
639
+ end
640
+
641
+ # Builds an AlterTable object for adding a column to a table.
642
+ #
643
+ # This definition object contains information about the column that would be created
644
+ # if the same arguments were passed to #add_column. See #add_column for information about
645
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
646
+ def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
618
647
  return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
619
648
 
620
649
  if supports_datetime_with_precision?
@@ -623,15 +652,9 @@ module ActiveRecord
623
652
  end
624
653
  end
625
654
 
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
655
+ alter_table = create_alter_table(table_name)
656
+ alter_table.add_column(column_name, type, **options)
657
+ alter_table
635
658
  end
636
659
 
637
660
  # Removes the given columns from the table definition.
@@ -662,7 +685,7 @@ module ActiveRecord
662
685
  #
663
686
  # If the options provided include an +if_exists+ key, it will be used to check if the
664
687
  # column does not exist. This will silently ignore the migration rather than raising
665
- # if the column was already used.
688
+ # if the column was already removed.
666
689
  #
667
690
  # remove_column(:suppliers, :qualification, if_exists: true)
668
691
  def remove_column(table_name, column_name, type = nil, **options)
@@ -699,6 +722,15 @@ module ActiveRecord
699
722
  raise NotImplementedError, "change_column_default is not implemented"
700
723
  end
701
724
 
725
+ # Builds a ChangeColumnDefaultDefinition object.
726
+ #
727
+ # This definition object contains information about the column change that would occur
728
+ # if the same arguments were passed to #change_column_default. See #change_column_default for
729
+ # information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
730
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
731
+ raise NotImplementedError, "build_change_column_default_definition is not implemented"
732
+ end
733
+
702
734
  # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
703
735
  # indicates whether the value can be +NULL+. For example
704
736
  #
@@ -805,6 +837,16 @@ module ActiveRecord
805
837
  #
806
838
  # Note: Partial indexes are only supported for PostgreSQL and SQLite.
807
839
  #
840
+ # ====== Creating an index that includes additional columns
841
+ #
842
+ # add_index(:accounts, :branch_id, include: :party_id)
843
+ #
844
+ # generates:
845
+ #
846
+ # CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
847
+ #
848
+ # Note: only supported by PostgreSQL.
849
+ #
808
850
  # ====== Creating an index with a specific method
809
851
  #
810
852
  # add_index(:developers, :name, using: 'btree')
@@ -842,20 +884,31 @@ module ActiveRecord
842
884
  # ====== Creating an index with a specific algorithm
843
885
  #
844
886
  # add_index(:developers, :name, algorithm: :concurrently)
845
- # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
887
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
846
888
  #
847
- # Note: only supported by PostgreSQL.
889
+ # add_index(:developers, :name, algorithm: :inplace)
890
+ # # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
891
+ #
892
+ # Note: only supported by PostgreSQL and MySQL.
848
893
  #
849
894
  # Concurrently adding an index is not supported in a transaction.
850
895
  #
851
896
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
852
897
  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)
898
+ create_index = build_create_index_definition(table_name, column_name, **options)
856
899
  execute schema_creation.accept(create_index)
857
900
  end
858
901
 
902
+ # Builds a CreateIndexDefinition object.
903
+ #
904
+ # This definition object contains information about the index that would be created
905
+ # if the same arguments were passed to #add_index. See #add_index for information about
906
+ # passing a +table_name+, +column_name+, and other additional options that can be passed.
907
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
908
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
909
+ CreateIndexDefinition.new(index, algorithm, if_not_exists)
910
+ end
911
+
859
912
  # Removes the given index from the table.
860
913
  #
861
914
  # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
@@ -921,7 +974,11 @@ module ActiveRecord
921
974
  def index_name(table_name, options) # :nodoc:
922
975
  if Hash === options
923
976
  if options[:column]
924
- "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
977
+ if options[:_uses_legacy_index_name]
978
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
979
+ else
980
+ generate_index_name(table_name, options[:column])
981
+ end
925
982
  elsif options[:name]
926
983
  options[:name]
927
984
  else
@@ -941,7 +998,6 @@ module ActiveRecord
941
998
  # Adds a reference. The reference column is a bigint by default,
942
999
  # the <tt>:type</tt> option can be used to specify a different type.
943
1000
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
944
- # #add_reference and #add_belongs_to are acceptable.
945
1001
  #
946
1002
  # The +options+ hash can include the following keys:
947
1003
  # [<tt>:type</tt>]
@@ -987,12 +1043,11 @@ module ActiveRecord
987
1043
  # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
988
1044
  #
989
1045
  def add_reference(table_name, ref_name, **options)
990
- ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
1046
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
991
1047
  end
992
1048
  alias :add_belongs_to :add_reference
993
1049
 
994
1050
  # Removes the reference(s). Also removes a +type+ column if one exists.
995
- # #remove_reference and #remove_belongs_to are acceptable.
996
1051
  #
997
1052
  # ====== Remove the reference
998
1053
  #
@@ -1007,19 +1062,21 @@ module ActiveRecord
1007
1062
  # remove_reference(:products, :user, foreign_key: true)
1008
1063
  #
1009
1064
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1065
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1066
+
1010
1067
  if foreign_key
1011
1068
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
1012
1069
  if foreign_key.is_a?(Hash)
1013
- foreign_key_options = foreign_key
1070
+ foreign_key_options = foreign_key.merge(conditional_options)
1014
1071
  else
1015
- foreign_key_options = { to_table: reference_name }
1072
+ foreign_key_options = { to_table: reference_name, **conditional_options }
1016
1073
  end
1017
1074
  foreign_key_options[:column] ||= "#{ref_name}_id"
1018
1075
  remove_foreign_key(table_name, **foreign_key_options)
1019
1076
  end
1020
1077
 
1021
- remove_column(table_name, "#{ref_name}_id")
1022
- remove_column(table_name, "#{ref_name}_type") if polymorphic
1078
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1079
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
1023
1080
  end
1024
1081
  alias :remove_belongs_to :remove_reference
1025
1082
 
@@ -1056,6 +1113,16 @@ module ActiveRecord
1056
1113
  #
1057
1114
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
1058
1115
  #
1116
+ # ====== Creating a composite foreign key
1117
+ #
1118
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1119
+ #
1120
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1121
+ #
1122
+ # generates:
1123
+ #
1124
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1125
+ #
1059
1126
  # ====== Creating a cascading foreign key
1060
1127
  #
1061
1128
  # add_foreign_key :articles, :authors, on_delete: :cascade
@@ -1066,9 +1133,11 @@ module ActiveRecord
1066
1133
  #
1067
1134
  # The +options+ hash can include the following keys:
1068
1135
  # [<tt>:column</tt>]
1069
- # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
1136
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1137
+ # Pass an array to create a composite foreign key.
1070
1138
  # [<tt>:primary_key</tt>]
1071
1139
  # The primary key column name on +to_table+. Defaults to +id+.
1140
+ # Pass an array to create a composite foreign key.
1072
1141
  # [<tt>:name</tt>]
1073
1142
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
1074
1143
  # [<tt>:on_delete</tt>]
@@ -1084,8 +1153,8 @@ module ActiveRecord
1084
1153
  # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1085
1154
  # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1086
1155
  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)
1156
+ return unless use_foreign_keys?
1157
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1089
1158
 
1090
1159
  options = foreign_key_options(from_table, to_table, options)
1091
1160
  at = create_alter_table from_table
@@ -1125,7 +1194,7 @@ module ActiveRecord
1125
1194
  # [<tt>:to_table</tt>]
1126
1195
  # The name of the table that contains the referenced primary key.
1127
1196
  def remove_foreign_key(from_table, to_table = nil, **options)
1128
- return unless supports_foreign_keys?
1197
+ return unless use_foreign_keys?
1129
1198
  return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1130
1199
 
1131
1200
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
@@ -1151,15 +1220,33 @@ module ActiveRecord
1151
1220
  foreign_key_for(from_table, to_table: to_table, **options).present?
1152
1221
  end
1153
1222
 
1154
- def foreign_key_column_for(table_name) # :nodoc:
1223
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1155
1224
  name = strip_table_name_prefix_and_suffix(table_name)
1156
- "#{name.singularize}_id"
1225
+ "#{name.singularize}_#{column_name}"
1157
1226
  end
1158
1227
 
1159
1228
  def foreign_key_options(from_table, to_table, options) # :nodoc:
1160
1229
  options = options.dup
1161
- options[:column] ||= foreign_key_column_for(to_table)
1230
+
1231
+ if options[:primary_key].is_a?(Array)
1232
+ options[:column] ||= options[:primary_key].map do |pk_column|
1233
+ foreign_key_column_for(to_table, pk_column)
1234
+ end
1235
+ else
1236
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1237
+ end
1238
+
1162
1239
  options[:name] ||= foreign_key_name(from_table, options)
1240
+
1241
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1242
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1243
+ raise ArgumentError, <<~MSG.squish
1244
+ For composite primary keys, specify :column and :primary_key, where
1245
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1246
+ MSG
1247
+ end
1248
+ end
1249
+
1163
1250
  options
1164
1251
  end
1165
1252
 
@@ -1181,12 +1268,16 @@ module ActiveRecord
1181
1268
  # The +options+ hash can include the following keys:
1182
1269
  # [<tt>:name</tt>]
1183
1270
  # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1271
+ # [<tt>:if_not_exists</tt>]
1272
+ # Silently ignore if the constraint already exists, rather than raise an error.
1184
1273
  # [<tt>:validate</tt>]
1185
1274
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1186
- def add_check_constraint(table_name, expression, **options)
1275
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1187
1276
  return unless supports_check_constraints?
1188
1277
 
1189
1278
  options = check_constraint_options(table_name, expression, options)
1279
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1280
+
1190
1281
  at = create_alter_table(table_name)
1191
1282
  at.add_check_constraint(expression, options)
1192
1283
 
@@ -1199,16 +1290,24 @@ module ActiveRecord
1199
1290
  options
1200
1291
  end
1201
1292
 
1202
- # Removes the given check constraint from the table.
1293
+ # Removes the given check constraint from the table. Removing a check constraint
1294
+ # that does not exist will raise an error.
1203
1295
  #
1204
1296
  # remove_check_constraint :products, name: "price_check"
1205
1297
  #
1298
+ # To silently ignore a non-existent check constraint rather than raise an error,
1299
+ # use the +if_exists+ option.
1300
+ #
1301
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1302
+ #
1206
1303
  # The +expression+ parameter will be ignored if present. It can be helpful
1207
1304
  # to provide this in a migration's +change+ method so it can be reverted.
1208
1305
  # In that case, +expression+ will be used by #add_check_constraint.
1209
- def remove_check_constraint(table_name, expression = nil, **options)
1306
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1210
1307
  return unless supports_check_constraints?
1211
1308
 
1309
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1310
+
1212
1311
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1213
1312
 
1214
1313
  at = create_alter_table(table_name)
@@ -1217,8 +1316,20 @@ module ActiveRecord
1217
1316
  execute schema_creation.accept(at)
1218
1317
  end
1219
1318
 
1319
+
1320
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1321
+ #
1322
+ # check_constraint_exists?(:products, name: "price_check")
1323
+ #
1324
+ def check_constraint_exists?(table_name, **options)
1325
+ if !options.key?(:name) && !options.key?(:expression)
1326
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1327
+ end
1328
+ check_constraint_for(table_name, **options).present?
1329
+ end
1330
+
1220
1331
  def dump_schema_information # :nodoc:
1221
- versions = schema_migration.all_versions
1332
+ versions = pool.schema_migration.versions
1222
1333
  insert_versions_sql(versions) if versions.any?
1223
1334
  end
1224
1335
 
@@ -1228,8 +1339,9 @@ module ActiveRecord
1228
1339
 
1229
1340
  def assume_migrated_upto_version(version)
1230
1341
  version = version.to_i
1231
- sm_table = quote_table_name(schema_migration.table_name)
1342
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1232
1343
 
1344
+ migration_context = pool.migration_context
1233
1345
  migrated = migration_context.get_all_versions
1234
1346
  versions = migration_context.migrations.map(&:version)
1235
1347
 
@@ -1291,18 +1403,24 @@ module ActiveRecord
1291
1403
  end
1292
1404
 
1293
1405
  def distinct_relation_for_primary_key(relation) # :nodoc:
1406
+ primary_key_columns = Array(relation.primary_key).map do |column|
1407
+ visitor.compile(relation.table[column])
1408
+ end
1409
+
1294
1410
  values = columns_for_distinct(
1295
- visitor.compile(relation.table[relation.primary_key]),
1411
+ primary_key_columns,
1296
1412
  relation.order_values
1297
1413
  )
1298
1414
 
1299
1415
  limited = relation.reselect(values).distinct!
1300
- limited_ids = select_rows(limited.arel, "SQL").map(&:last)
1416
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1417
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
1418
+ end
1301
1419
 
1302
1420
  if limited_ids.empty?
1303
1421
  relation.none!
1304
1422
  else
1305
- relation.where!(relation.primary_key => limited_ids)
1423
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1306
1424
  end
1307
1425
 
1308
1426
  relation.limit_value = relation.offset_value = nil
@@ -1315,14 +1433,8 @@ module ActiveRecord
1315
1433
  # add_timestamps(:suppliers, null: true)
1316
1434
  #
1317
1435
  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
1436
+ fragments = add_timestamps_for_alter(table_name, **options)
1437
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1326
1438
  end
1327
1439
 
1328
1440
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
@@ -1338,7 +1450,7 @@ module ActiveRecord
1338
1450
  end
1339
1451
 
1340
1452
  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)
1453
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1342
1454
 
1343
1455
  column_names = index_column_names(column_name)
1344
1456
 
@@ -1357,6 +1469,8 @@ module ActiveRecord
1357
1469
  where: options[:where],
1358
1470
  type: options[:type],
1359
1471
  using: options[:using],
1472
+ include: options[:include],
1473
+ nulls_not_distinct: options[:nulls_not_distinct],
1360
1474
  comment: options[:comment]
1361
1475
  )
1362
1476
 
@@ -1404,7 +1518,79 @@ module ActiveRecord
1404
1518
  SchemaDumper.create(self, options)
1405
1519
  end
1406
1520
 
1521
+ def use_foreign_keys?
1522
+ supports_foreign_keys? && foreign_keys_enabled?
1523
+ end
1524
+
1525
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1526
+ # object and return DDL.
1527
+ def schema_creation # :nodoc:
1528
+ SchemaCreation.new(self)
1529
+ end
1530
+
1531
+ def bulk_change_table(table_name, operations) # :nodoc:
1532
+ sql_fragments = []
1533
+ non_combinable_operations = []
1534
+
1535
+ operations.each do |command, args|
1536
+ table, arguments = args.shift, args
1537
+ method = :"#{command}_for_alter"
1538
+
1539
+ if respond_to?(method, true)
1540
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1541
+ sql_fragments.concat(sqls)
1542
+ non_combinable_operations.concat(procs)
1543
+ else
1544
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1545
+ non_combinable_operations.each(&:call)
1546
+ sql_fragments = []
1547
+ non_combinable_operations = []
1548
+ send(command, table, *arguments)
1549
+ end
1550
+ end
1551
+
1552
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1553
+ non_combinable_operations.each(&:call)
1554
+ end
1555
+
1556
+ def valid_table_definition_options # :nodoc:
1557
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1558
+ end
1559
+
1560
+ def valid_column_definition_options # :nodoc:
1561
+ ColumnDefinition::OPTION_NAMES
1562
+ end
1563
+
1564
+ def valid_primary_key_options # :nodoc:
1565
+ [:limit, :default, :precision]
1566
+ end
1567
+
1568
+ # Returns the maximum length of an index name in bytes.
1569
+ def max_index_name_size
1570
+ 62
1571
+ end
1572
+
1407
1573
  private
1574
+ def generate_index_name(table_name, column)
1575
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1576
+ return name if name.bytesize <= max_index_name_size
1577
+
1578
+ # Fallback to short version, add hash to ensure uniqueness
1579
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1580
+ name = "idx_on_#{Array(column) * '_'}"
1581
+
1582
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1583
+ short_name = name.mb_chars.limit(short_limit).to_s
1584
+
1585
+ "#{short_name}#{hashed_identifier}"
1586
+ end
1587
+
1588
+ def validate_change_column_null_argument!(value)
1589
+ unless value == true || value == false
1590
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1591
+ end
1592
+ end
1593
+
1408
1594
  def column_options_keys
1409
1595
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
1410
1596
  end
@@ -1458,7 +1644,7 @@ module ActiveRecord
1458
1644
 
1459
1645
  if matching_indexes.count > 1
1460
1646
  raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1461
- "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1647
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1462
1648
  elsif matching_indexes.none?
1463
1649
  raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1464
1650
  else
@@ -1466,11 +1652,11 @@ module ActiveRecord
1466
1652
  end
1467
1653
  end
1468
1654
 
1469
- def rename_table_indexes(table_name, new_name)
1655
+ def rename_table_indexes(table_name, new_name, **options)
1470
1656
  indexes(new_name).each do |index|
1471
- generated_index_name = index_name(table_name, column: index.columns)
1657
+ generated_index_name = index_name(table_name, column: index.columns, **options)
1472
1658
  if generated_index_name == index.name
1473
- rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
1659
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
1474
1660
  end
1475
1661
  end
1476
1662
  end
@@ -1488,10 +1674,6 @@ module ActiveRecord
1488
1674
  end
1489
1675
  end
1490
1676
 
1491
- def schema_creation
1492
- SchemaCreation.new(self)
1493
- end
1494
-
1495
1677
  def create_table_definition(name, **options)
1496
1678
  TableDefinition.new(self, name, **options)
1497
1679
  end
@@ -1500,8 +1682,12 @@ module ActiveRecord
1500
1682
  AlterTable.new create_table_definition(name)
1501
1683
  end
1502
1684
 
1503
- def extract_table_options!(options)
1504
- options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1685
+ def validate_create_table_options!(options)
1686
+ unless options[:_skip_validate_options]
1687
+ options
1688
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1689
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1690
+ end
1505
1691
  end
1506
1692
 
1507
1693
  def fetch_type_metadata(sql_type)
@@ -1544,7 +1730,8 @@ module ActiveRecord
1544
1730
 
1545
1731
  def foreign_key_name(table_name, options)
1546
1732
  options.fetch(:name) do
1547
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1733
+ columns = Array(options.fetch(:column)).map(&:to_s)
1734
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1548
1735
  hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1549
1736
 
1550
1737
  "fk_rails_#{hashed_identifier}"
@@ -1552,7 +1739,7 @@ module ActiveRecord
1552
1739
  end
1553
1740
 
1554
1741
  def foreign_key_for(from_table, **options)
1555
- return unless supports_foreign_keys?
1742
+ return unless use_foreign_keys?
1556
1743
  foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1557
1744
  end
1558
1745
 
@@ -1569,6 +1756,10 @@ module ActiveRecord
1569
1756
  end
1570
1757
  end
1571
1758
 
1759
+ def foreign_keys_enabled?
1760
+ @config.fetch(:foreign_keys, true)
1761
+ end
1762
+
1572
1763
  def check_constraint_name(table_name, **options)
1573
1764
  options.fetch(:name) do
1574
1765
  expression = options.fetch(:expression)
@@ -1582,7 +1773,7 @@ module ActiveRecord
1582
1773
  def check_constraint_for(table_name, **options)
1583
1774
  return unless supports_check_constraints?
1584
1775
  chk_name = check_constraint_name(table_name, **options)
1585
- check_constraints(table_name).detect { |chk| chk.name == chk_name }
1776
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1586
1777
  end
1587
1778
 
1588
1779
  def check_constraint_for!(table_name, expression: nil, **options)
@@ -1596,6 +1787,12 @@ module ActiveRecord
1596
1787
  end
1597
1788
  end
1598
1789
 
1790
+ def validate_table_length!(table_name)
1791
+ if table_name.length > table_name_length
1792
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1793
+ end
1794
+ end
1795
+
1599
1796
  def extract_new_default_value(default_or_changes)
1600
1797
  if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1601
1798
  default_or_changes[:to]
@@ -1609,29 +1806,8 @@ module ActiveRecord
1609
1806
  column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1610
1807
  end
1611
1808
 
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)
1809
+ def reference_name_for_table(table_name)
1810
+ table_name.to_s.singularize
1635
1811
  end
1636
1812
 
1637
1813
  def add_column_for_alter(table_name, column_name, type, **options)
@@ -1640,6 +1816,11 @@ module ActiveRecord
1640
1816
  schema_creation.accept(AddColumnDefinition.new(cd))
1641
1817
  end
1642
1818
 
1819
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1820
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1821
+ schema_creation.accept(cd)
1822
+ end
1823
+
1643
1824
  def rename_column_sql(table_name, column_name, new_column_name)
1644
1825
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1645
1826
  end
@@ -1670,12 +1851,12 @@ module ActiveRecord
1670
1851
  end
1671
1852
 
1672
1853
  def insert_versions_sql(versions)
1673
- sm_table = quote_table_name(schema_migration.table_name)
1854
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1674
1855
 
1675
1856
  if versions.is_a?(Array)
1676
1857
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1677
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1678
- sql << ";\n\n"
1858
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1859
+ sql << ";"
1679
1860
  sql
1680
1861
  else
1681
1862
  "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"