activerecord 7.0.0 → 7.1.0

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