activerecord 6.0.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +872 -582
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +1 -2
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations.rb +116 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +32 -8
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +86 -37
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +4 -9
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -76
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  102. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  103. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  104. data/lib/active_record/connection_adapters/postgresql_adapter.rb +81 -57
  105. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  106. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  107. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  108. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  109. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  110. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  111. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  112. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  113. data/lib/active_record/connection_handling.rb +211 -81
  114. data/lib/active_record/core.rb +237 -69
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations.rb +124 -85
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  118. data/lib/active_record/database_configurations/database_config.rb +52 -9
  119. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  120. data/lib/active_record/database_configurations/url_config.rb +15 -41
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +40 -16
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +54 -11
  134. data/lib/active_record/gem_version.rb +1 -1
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +39 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +22 -17
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +27 -9
  143. data/lib/active_record/middleware/database_selector.rb +4 -2
  144. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  145. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  146. data/lib/active_record/migration.rb +114 -84
  147. data/lib/active_record/migration/command_recorder.rb +53 -45
  148. data/lib/active_record/migration/compatibility.rb +70 -20
  149. data/lib/active_record/migration/join_table.rb +0 -1
  150. data/lib/active_record/model_schema.rb +120 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/databases.rake +267 -93
  159. data/lib/active_record/readonly_attributes.rb +4 -0
  160. data/lib/active_record/reflection.rb +77 -63
  161. data/lib/active_record/relation.rb +108 -67
  162. data/lib/active_record/relation/batches.rb +38 -32
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  164. data/lib/active_record/relation/calculations.rb +102 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +55 -17
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder.rb +55 -35
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  172. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  173. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  174. data/lib/active_record/relation/query_methods.rb +340 -180
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +104 -58
  178. data/lib/active_record/result.rb +41 -34
  179. data/lib/active_record/runtime_registry.rb +2 -2
  180. data/lib/active_record/sanitization.rb +6 -17
  181. data/lib/active_record/schema_dumper.rb +34 -4
  182. data/lib/active_record/schema_migration.rb +2 -8
  183. data/lib/active_record/scoping.rb +0 -1
  184. data/lib/active_record/scoping/default.rb +0 -1
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/secure_token.rb +16 -8
  187. data/lib/active_record/serialization.rb +5 -3
  188. data/lib/active_record/signed_id.rb +116 -0
  189. data/lib/active_record/statement_cache.rb +20 -4
  190. data/lib/active_record/store.rb +3 -3
  191. data/lib/active_record/suppressor.rb +2 -2
  192. data/lib/active_record/table_metadata.rb +39 -36
  193. data/lib/active_record/tasks/database_tasks.rb +139 -113
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  196. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  197. data/lib/active_record/test_databases.rb +5 -4
  198. data/lib/active_record/test_fixtures.rb +38 -16
  199. data/lib/active_record/timestamp.rb +4 -7
  200. data/lib/active_record/touch_later.rb +20 -21
  201. data/lib/active_record/transactions.rb +22 -71
  202. data/lib/active_record/type.rb +8 -2
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type_caster/connection.rb +0 -1
  210. data/lib/active_record/type_caster/map.rb +8 -5
  211. data/lib/active_record/validations.rb +3 -3
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/arel.rb +15 -12
  216. data/lib/arel/attributes/attribute.rb +4 -0
  217. data/lib/arel/collectors/bind.rb +5 -0
  218. data/lib/arel/collectors/composite.rb +8 -0
  219. data/lib/arel/collectors/sql_string.rb +7 -0
  220. data/lib/arel/collectors/substitute_binds.rb +7 -0
  221. data/lib/arel/nodes.rb +3 -1
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/predications.rb +17 -24
  237. data/lib/arel/select_manager.rb +1 -2
  238. data/lib/arel/table.rb +13 -5
  239. data/lib/arel/visitors.rb +0 -7
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration.rb +6 -2
  248. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  249. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  250. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +27 -24
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -13,9 +13,9 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def column_spec_for_primary_key(column)
16
- return {} if default_primary_key?(column)
17
- spec = { id: schema_type(column).inspect }
18
- spec.merge!(prepare_column_options(column).except!(:null, :comment))
16
+ spec = {}
17
+ spec[:id] = schema_type(column).inspect unless default_primary_key?(column)
18
+ spec.merge!(prepare_column_options(column).except!(:null))
19
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
20
  spec
21
21
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record/migration/join_table"
4
3
  require "active_support/core_ext/string/access"
5
- require "active_support/deprecation"
6
4
  require "digest/sha2"
7
5
 
8
6
  module ActiveRecord
@@ -98,10 +96,14 @@ module ActiveRecord
98
96
  # # Check an index with a custom name exists
99
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
100
98
  #
101
- def index_exists?(table_name, column_name, options = {})
102
- column_names = Array(column_name).map(&:to_s)
99
+ def index_exists?(table_name, column_name, **options)
103
100
  checks = []
104
- checks << lambda { |i| Array(i.columns) == column_names }
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
+
105
107
  checks << lambda { |i| i.unique } if options[:unique]
106
108
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
109
 
@@ -291,37 +293,42 @@ module ActiveRecord
291
293
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
292
294
  #
293
295
  # See also TableDefinition#column for details on how to create columns.
294
- def create_table(table_name, **options)
295
- td = create_table_definition(table_name, options)
296
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
297
+ td = create_table_definition(table_name, **extract_table_options!(options))
298
+
299
+ if id && !td.as
300
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
296
301
 
297
- if options[:id] != false && !options[:as]
298
- pk = options.fetch(:primary_key) do
299
- Base.get_primary_key table_name.to_s.singularize
302
+ if id.is_a?(Hash)
303
+ options.merge!(id.except(:type))
304
+ id = id.fetch(:type, :primary_key)
300
305
  end
301
306
 
302
307
  if pk.is_a?(Array)
303
308
  td.primary_keys pk
304
309
  else
305
- td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
310
+ td.primary_key pk, id, **options
306
311
  end
307
312
  end
308
313
 
309
314
  yield td if block_given?
310
315
 
311
- if options[:force]
312
- drop_table(table_name, options.merge(if_exists: true))
316
+ if force
317
+ drop_table(table_name, force: force, if_exists: true)
318
+ else
319
+ schema_cache.clear_data_source_cache!(table_name.to_s)
313
320
  end
314
321
 
315
322
  result = execute schema_creation.accept td
316
323
 
317
324
  unless supports_indexes_in_create?
318
325
  td.indexes.each do |column_name, index_options|
319
- add_index(table_name, column_name, index_options)
326
+ add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
320
327
  end
321
328
  end
322
329
 
323
330
  if supports_comments? && !supports_comments_in_create?
324
- if table_comment = options[:comment].presence
331
+ if table_comment = td.comment.presence
325
332
  change_table_comment(table_name, table_comment)
326
333
  end
327
334
 
@@ -378,9 +385,9 @@ module ActiveRecord
378
385
 
379
386
  t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
380
387
 
381
- create_table(join_table_name, options.merge!(id: false)) do |td|
382
- td.references t1_ref, column_options
383
- td.references t2_ref, column_options
388
+ create_table(join_table_name, **options.merge!(id: false)) do |td|
389
+ td.references t1_ref, **column_options
390
+ td.references t2_ref, **column_options
384
391
  yield td if block_given?
385
392
  end
386
393
  end
@@ -391,7 +398,7 @@ module ActiveRecord
391
398
  # Although this command ignores the block if one is given, it can be helpful
392
399
  # to provide one in a migration's +change+ method so it can be reverted.
393
400
  # In that case, the block will be used by #create_join_table.
394
- def drop_join_table(table_1, table_2, options = {})
401
+ def drop_join_table(table_1, table_2, **options)
395
402
  join_table_name = find_join_table_name(table_1, table_2, options)
396
403
  drop_table(join_table_name)
397
404
  end
@@ -420,6 +427,12 @@ module ActiveRecord
420
427
  # t.column :name, :string, limit: 60
421
428
  # end
422
429
  #
430
+ # ====== Change type of a column
431
+ #
432
+ # change_table(:suppliers) do |t|
433
+ # t.change :metadata, :json
434
+ # end
435
+ #
423
436
  # ====== Add 2 integer columns
424
437
  #
425
438
  # change_table(:suppliers) do |t|
@@ -468,7 +481,7 @@ module ActiveRecord
468
481
  # end
469
482
  #
470
483
  # See also Table for details on all of the various column transformations.
471
- def change_table(table_name, options = {})
484
+ def change_table(table_name, **options)
472
485
  if supports_bulk_alter? && options[:bulk]
473
486
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
474
487
  yield update_table_definition(table_name, recorder)
@@ -498,7 +511,8 @@ module ActiveRecord
498
511
  # Although this command ignores most +options+ and the block if one is given,
499
512
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
500
513
  # In that case, +options+ and the block will be used by #create_table.
501
- def drop_table(table_name, options = {})
514
+ def drop_table(table_name, **options)
515
+ schema_cache.clear_data_source_cache!(table_name.to_s)
502
516
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
503
517
  end
504
518
 
@@ -534,6 +548,9 @@ module ActiveRecord
534
548
  # column will have the same collation as the table.
535
549
  # * <tt>:comment</tt> -
536
550
  # Specifies the comment for the column. This option is ignored by some backends.
551
+ # * <tt>:if_not_exists</tt> -
552
+ # Specifies if the column already exists to not try to re-add it. This will avoid
553
+ # duplicate column errors.
537
554
  #
538
555
  # Note: The precision is the total number of significant digits,
539
556
  # and the scale is the number of digits that can be stored following
@@ -554,8 +571,6 @@ module ActiveRecord
554
571
  # but the maximum supported <tt>:precision</tt> is 16. No default.
555
572
  # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
556
573
  # Default is (38,0).
557
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
558
- # Default unknown.
559
574
  # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
560
575
  # Default (38,0).
561
576
  #
@@ -585,20 +600,37 @@ module ActiveRecord
585
600
  # # Defines a column with a database-specific type.
586
601
  # add_column(:shapes, :triangle, 'polygon')
587
602
  # # ALTER TABLE "shapes" ADD "triangle" polygon
603
+ #
604
+ # # Ignores the method call if the column exists
605
+ # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
588
606
  def add_column(table_name, column_name, type, **options)
607
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
608
+
589
609
  at = create_alter_table table_name
590
- at.add_column(column_name, type, options)
610
+ at.add_column(column_name, type, **options)
591
611
  execute schema_creation.accept at
592
612
  end
593
613
 
614
+ def add_columns(table_name, *column_names, type:, **options) # :nodoc:
615
+ column_names.each do |column_name|
616
+ add_column(table_name, column_name, type, **options)
617
+ end
618
+ end
619
+
594
620
  # Removes the given columns from the table definition.
595
621
  #
596
622
  # remove_columns(:suppliers, :qualification, :experience)
597
623
  #
598
- def remove_columns(table_name, *column_names)
599
- raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
624
+ # +type+ and other column options can be passed to make migration reversible.
625
+ #
626
+ # remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
627
+ def remove_columns(table_name, *column_names, type: nil, **options)
628
+ if column_names.empty?
629
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
630
+ end
631
+
600
632
  column_names.each do |column_name|
601
- remove_column(table_name, column_name)
633
+ remove_column(table_name, column_name, type, **options)
602
634
  end
603
635
  end
604
636
 
@@ -610,8 +642,16 @@ module ActiveRecord
610
642
  # to provide these in a migration's +change+ method so it can be reverted.
611
643
  # In that case, +type+ and +options+ will be used by #add_column.
612
644
  # Indexes on the column are automatically removed.
613
- def remove_column(table_name, column_name, type = nil, options = {})
614
- execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
645
+ #
646
+ # If the options provided include an +if_exists+ key, it will be used to check if the
647
+ # column does not exist. This will silently ignore the migration rather than raising
648
+ # if the column was already used.
649
+ #
650
+ # remove_column(:suppliers, :qualification, if_exists: true)
651
+ def remove_column(table_name, column_name, type = nil, **options)
652
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
653
+
654
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
615
655
  end
616
656
 
617
657
  # Changes the column's definition according to the new options.
@@ -620,7 +660,7 @@ module ActiveRecord
620
660
  # change_column(:suppliers, :name, :string, limit: 80)
621
661
  # change_column(:accounts, :description, :text)
622
662
  #
623
- def change_column(table_name, column_name, type, options = {})
663
+ def change_column(table_name, column_name, type, **options)
624
664
  raise NotImplementedError, "change_column is not implemented"
625
665
  end
626
666
 
@@ -682,7 +722,17 @@ module ActiveRecord
682
722
  #
683
723
  # generates:
684
724
  #
685
- # CREATE INDEX suppliers_name_index ON suppliers(name)
725
+ # CREATE INDEX index_suppliers_on_name ON suppliers(name)
726
+ #
727
+ # ====== Creating a index which already exists
728
+ #
729
+ # add_index(:suppliers, :name, if_not_exists: true)
730
+ #
731
+ # generates:
732
+ #
733
+ # CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
734
+ #
735
+ # Note: Not supported by MySQL.
686
736
  #
687
737
  # ====== Creating a unique index
688
738
  #
@@ -690,7 +740,7 @@ module ActiveRecord
690
740
  #
691
741
  # generates:
692
742
  #
693
- # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
743
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
694
744
  #
695
745
  # ====== Creating a named index
696
746
  #
@@ -720,7 +770,7 @@ module ActiveRecord
720
770
  #
721
771
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
722
772
  #
723
- # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
773
+ # add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
724
774
  #
725
775
  # generates:
726
776
  #
@@ -736,7 +786,7 @@ module ActiveRecord
736
786
  #
737
787
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
738
788
  #
739
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
789
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
740
790
  #
741
791
  # ====== Creating an index with a specific method
742
792
  #
@@ -782,9 +832,11 @@ module ActiveRecord
782
832
  # Concurrently adding an index is not supported in a transaction.
783
833
  #
784
834
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
785
- def add_index(table_name, column_name, options = {})
786
- index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
787
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
835
+ def add_index(table_name, column_name, **options)
836
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
837
+
838
+ create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
839
+ execute schema_creation.accept(create_index)
788
840
  end
789
841
 
790
842
  # Removes the given index from the table.
@@ -805,6 +857,15 @@ module ActiveRecord
805
857
  #
806
858
  # remove_index :accounts, name: :by_branch_party
807
859
  #
860
+ # Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
861
+ #
862
+ # remove_index :accounts, :branch_id, name: :by_branch_party
863
+ #
864
+ # Checks if the index exists before trying to remove it. Will silently ignore indexes that
865
+ # don't exist.
866
+ #
867
+ # remove_index :accounts, if_exists: true
868
+ #
808
869
  # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
870
  #
810
871
  # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
@@ -814,8 +875,11 @@ module ActiveRecord
814
875
  # Concurrently removing an index is not supported in a transaction.
815
876
  #
816
877
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
817
- def remove_index(table_name, options = {})
818
- index_name = index_name_for_remove(table_name, options)
878
+ def remove_index(table_name, column_name = nil, **options)
879
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
880
+
881
+ index_name = index_name_for_remove(table_name, column_name, options)
882
+
819
883
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
820
884
  end
821
885
 
@@ -826,6 +890,8 @@ module ActiveRecord
826
890
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
827
891
  #
828
892
  def rename_index(table_name, old_name, new_name)
893
+ old_name = old_name.to_s
894
+ new_name = new_name.to_s
829
895
  validate_index_length!(table_name, new_name)
830
896
 
831
897
  # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
@@ -867,7 +933,9 @@ module ActiveRecord
867
933
  # Add an appropriate index. Defaults to true.
868
934
  # See #add_index for usage of this option.
869
935
  # [<tt>:foreign_key</tt>]
870
- # Add an appropriate foreign key constraint. Defaults to false.
936
+ # Add an appropriate foreign key constraint. Defaults to false, pass true
937
+ # to add. In case the join table can't be inferred from the association
938
+ # pass <tt>:to_table</tt> with the appropriate table name.
871
939
  # [<tt>:polymorphic</tt>]
872
940
  # Whether an additional +_type+ column should be added. Defaults to false.
873
941
  # [<tt>:null</tt>]
@@ -899,10 +967,10 @@ module ActiveRecord
899
967
  #
900
968
  # ====== Create a supplier_id column and a foreign key to the firms table
901
969
  #
902
- # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
970
+ # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
903
971
  #
904
972
  def add_reference(table_name, ref_name, **options)
905
- ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
973
+ ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
906
974
  end
907
975
  alias :add_belongs_to :add_reference
908
976
 
@@ -930,7 +998,7 @@ module ActiveRecord
930
998
  foreign_key_options = { to_table: reference_name }
931
999
  end
932
1000
  foreign_key_options[:column] ||= "#{ref_name}_id"
933
- remove_foreign_key(table_name, foreign_key_options)
1001
+ remove_foreign_key(table_name, **foreign_key_options)
934
1002
  end
935
1003
 
936
1004
  remove_column(table_name, "#{ref_name}_id")
@@ -988,7 +1056,7 @@ module ActiveRecord
988
1056
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
989
1057
  # [<tt>:validate</tt>]
990
1058
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
991
- def add_foreign_key(from_table, to_table, options = {})
1059
+ def add_foreign_key(from_table, to_table, **options)
992
1060
  return unless supports_foreign_keys?
993
1061
 
994
1062
  options = foreign_key_options(from_table, to_table, options)
@@ -1061,6 +1129,60 @@ module ActiveRecord
1061
1129
  options
1062
1130
  end
1063
1131
 
1132
+ # Returns an array of check constraints for the given table.
1133
+ # The check constraints are represented as CheckConstraintDefinition objects.
1134
+ def check_constraints(table_name)
1135
+ raise NotImplementedError
1136
+ end
1137
+
1138
+ # Adds a new check constraint to the table. +expression+ is a String
1139
+ # representation of verifiable boolean condition.
1140
+ #
1141
+ # add_check_constraint :products, "price > 0", name: "price_check"
1142
+ #
1143
+ # generates:
1144
+ #
1145
+ # ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
1146
+ #
1147
+ # The +options+ hash can include the following keys:
1148
+ # [<tt>:name</tt>]
1149
+ # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1150
+ # [<tt>:validate</tt>]
1151
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1152
+ def add_check_constraint(table_name, expression, **options)
1153
+ return unless supports_check_constraints?
1154
+
1155
+ options = check_constraint_options(table_name, expression, options)
1156
+ at = create_alter_table(table_name)
1157
+ at.add_check_constraint(expression, options)
1158
+
1159
+ execute schema_creation.accept(at)
1160
+ end
1161
+
1162
+ def check_constraint_options(table_name, expression, options) # :nodoc:
1163
+ options = options.dup
1164
+ options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
1165
+ options
1166
+ end
1167
+
1168
+ # Removes the given check constraint from the table.
1169
+ #
1170
+ # remove_check_constraint :products, name: "price_check"
1171
+ #
1172
+ # The +expression+ parameter will be ignored if present. It can be helpful
1173
+ # to provide this in a migration's +change+ method so it can be reverted.
1174
+ # In that case, +expression+ will be used by #add_check_constraint.
1175
+ def remove_check_constraint(table_name, expression = nil, **options)
1176
+ return unless supports_check_constraints?
1177
+
1178
+ chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1179
+
1180
+ at = create_alter_table(table_name)
1181
+ at.drop_check_constraint(chk_name_to_delete)
1182
+
1183
+ execute schema_creation.accept(at)
1184
+ end
1185
+
1064
1186
  def dump_schema_information # :nodoc:
1065
1187
  versions = schema_migration.all_versions
1066
1188
  insert_versions_sql(versions) if versions.any?
@@ -1070,13 +1192,7 @@ module ActiveRecord
1070
1192
  { primary_key: true }
1071
1193
  end
1072
1194
 
1073
- def assume_migrated_upto_version(version, migrations_paths = nil)
1074
- unless migrations_paths.nil?
1075
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
1076
- Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1077
- MSG
1078
- end
1079
-
1195
+ def assume_migrated_upto_version(version)
1080
1196
  version = version.to_i
1081
1197
  sm_table = quote_table_name(schema_migration.table_name)
1082
1198
 
@@ -1145,22 +1261,22 @@ module ActiveRecord
1145
1261
  #
1146
1262
  # add_timestamps(:suppliers, null: true)
1147
1263
  #
1148
- def add_timestamps(table_name, options = {})
1264
+ def add_timestamps(table_name, **options)
1149
1265
  options[:null] = false if options[:null].nil?
1150
1266
 
1151
1267
  if !options.key?(:precision) && supports_datetime_with_precision?
1152
1268
  options[:precision] = 6
1153
1269
  end
1154
1270
 
1155
- add_column table_name, :created_at, :datetime, options
1156
- add_column table_name, :updated_at, :datetime, options
1271
+ add_column table_name, :created_at, :datetime, **options
1272
+ add_column table_name, :updated_at, :datetime, **options
1157
1273
  end
1158
1274
 
1159
1275
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1160
1276
  #
1161
1277
  # remove_timestamps(:suppliers)
1162
1278
  #
1163
- def remove_timestamps(table_name, options = {})
1279
+ def remove_timestamps(table_name, **options)
1164
1280
  remove_column table_name, :updated_at
1165
1281
  remove_column table_name, :created_at
1166
1282
  end
@@ -1169,36 +1285,43 @@ module ActiveRecord
1169
1285
  Table.new(table_name, base)
1170
1286
  end
1171
1287
 
1172
- def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1173
- column_names = index_column_names(column_name)
1288
+ def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1289
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
1174
1290
 
1175
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
1291
+ column_names = index_column_names(column_name)
1176
1292
 
1177
- index_type = options[:type].to_s if options.key?(:type)
1178
- index_type ||= options[:unique] ? "UNIQUE" : ""
1179
- index_name = options[:name].to_s if options.key?(:name)
1293
+ index_name = name&.to_s
1180
1294
  index_name ||= index_name(table_name, column_names)
1181
1295
 
1182
- if options.key?(:algorithm)
1183
- algorithm = index_algorithms.fetch(options[:algorithm]) {
1184
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
1185
- }
1186
- end
1187
-
1188
- using = "USING #{options[:using]}" if options[:using].present?
1189
-
1190
- if supports_partial_index?
1191
- index_options = options[:where] ? " WHERE #{options[:where]}" : ""
1192
- end
1296
+ validate_index_length!(table_name, index_name, internal)
1297
+
1298
+ index = IndexDefinition.new(
1299
+ table_name, index_name,
1300
+ options[:unique],
1301
+ column_names,
1302
+ lengths: options[:length] || {},
1303
+ orders: options[:order] || {},
1304
+ opclasses: options[:opclass] || {},
1305
+ where: options[:where],
1306
+ type: options[:type],
1307
+ using: options[:using],
1308
+ comment: options[:comment]
1309
+ )
1310
+
1311
+ [index, index_algorithm(options[:algorithm]), if_not_exists]
1312
+ end
1193
1313
 
1194
- validate_index_length!(table_name, index_name, options.fetch(:internal, false))
1314
+ def index_algorithm(algorithm) # :nodoc:
1315
+ index_algorithms.fetch(algorithm) do
1316
+ raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
1317
+ end if algorithm
1318
+ end
1195
1319
 
1196
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
1197
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
1320
+ def quoted_columns_for_index(column_names, options) # :nodoc:
1321
+ quoted_columns = column_names.each_with_object({}) do |name, result|
1322
+ result[name.to_sym] = quote_column_name(name).dup
1198
1323
  end
1199
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
1200
-
1201
- [index_name, index_type, index_columns, index_options, algorithm, using, comment]
1324
+ add_options_for_index_columns(quoted_columns, **options).values.join(", ")
1202
1325
  end
1203
1326
 
1204
1327
  def options_include_default?(options)
@@ -1253,30 +1376,19 @@ module ActiveRecord
1253
1376
  # the PostgreSQL adapter for supporting operator classes.
1254
1377
  def add_options_for_index_columns(quoted_columns, **options)
1255
1378
  if supports_index_sort_order?
1256
- quoted_columns = add_index_sort_order(quoted_columns, options)
1379
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
1257
1380
  end
1258
1381
 
1259
1382
  quoted_columns
1260
1383
  end
1261
1384
 
1262
- def quoted_columns_for_index(column_names, **options)
1263
- return [column_names] if column_names.is_a?(String)
1264
-
1265
- quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1266
- add_options_for_index_columns(quoted_columns, options).values
1267
- end
1268
-
1269
- def index_name_for_remove(table_name, options = {})
1270
- return options[:name] if can_remove_index_by_name?(options)
1385
+ def index_name_for_remove(table_name, column_name, options)
1386
+ return options[:name] if can_remove_index_by_name?(column_name, options)
1271
1387
 
1272
1388
  checks = []
1273
1389
 
1274
- if options.is_a?(Hash)
1275
- checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1276
- column_names = index_column_names(options[:column])
1277
- else
1278
- column_names = index_column_names(options)
1279
- end
1390
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1391
+ column_names = index_column_names(column_name || options[:column])
1280
1392
 
1281
1393
  if column_names.present?
1282
1394
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
@@ -1322,14 +1434,18 @@ module ActiveRecord
1322
1434
  SchemaCreation.new(self)
1323
1435
  end
1324
1436
 
1325
- def create_table_definition(*args)
1326
- TableDefinition.new(self, *args)
1437
+ def create_table_definition(name, **options)
1438
+ TableDefinition.new(self, name, **options)
1327
1439
  end
1328
1440
 
1329
1441
  def create_alter_table(name)
1330
1442
  AlterTable.new create_table_definition(name)
1331
1443
  end
1332
1444
 
1445
+ def extract_table_options!(options)
1446
+ options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1447
+ end
1448
+
1333
1449
  def fetch_type_metadata(sql_type)
1334
1450
  cast_type = lookup_cast_type(sql_type)
1335
1451
  SqlTypeMetadata.new(
@@ -1374,7 +1490,7 @@ module ActiveRecord
1374
1490
 
1375
1491
  def foreign_key_for(from_table, **options)
1376
1492
  return unless supports_foreign_keys?
1377
- foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1493
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1378
1494
  end
1379
1495
 
1380
1496
  def foreign_key_for!(from_table, to_table: nil, **options)
@@ -1390,11 +1506,30 @@ module ActiveRecord
1390
1506
  end
1391
1507
  end
1392
1508
 
1393
- def validate_index_length!(table_name, new_name, internal = false)
1394
- max_index_length = internal ? index_name_length : allowed_index_name_length
1509
+ def check_constraint_name(table_name, **options)
1510
+ options.fetch(:name) do
1511
+ expression = options.fetch(:expression)
1512
+ identifier = "#{table_name}_#{expression}_chk"
1513
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1395
1514
 
1396
- if new_name.length > max_index_length
1397
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1515
+ "chk_rails_#{hashed_identifier}"
1516
+ end
1517
+ end
1518
+
1519
+ def check_constraint_for(table_name, **options)
1520
+ return unless supports_check_constraints?
1521
+ chk_name = check_constraint_name(table_name, **options)
1522
+ check_constraints(table_name).detect { |chk| chk.name == chk_name }
1523
+ end
1524
+
1525
+ def check_constraint_for!(table_name, expression: nil, **options)
1526
+ check_constraint_for(table_name, expression: expression, **options) ||
1527
+ raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
1528
+ end
1529
+
1530
+ def validate_index_length!(table_name, new_name, internal = false)
1531
+ if new_name.length > index_name_length
1532
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
1398
1533
  end
1399
1534
  end
1400
1535
 
@@ -1407,8 +1542,8 @@ module ActiveRecord
1407
1542
  end
1408
1543
  alias :extract_new_comment_value :extract_new_default_value
1409
1544
 
1410
- def can_remove_index_by_name?(options)
1411
- options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1545
+ def can_remove_index_by_name?(column_name, options)
1546
+ column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1412
1547
  end
1413
1548
 
1414
1549
  def bulk_change_table(table_name, operations)
@@ -1436,20 +1571,41 @@ module ActiveRecord
1436
1571
  non_combinable_operations.each(&:call)
1437
1572
  end
1438
1573
 
1439
- def add_column_for_alter(table_name, column_name, type, options = {})
1574
+ def add_column_for_alter(table_name, column_name, type, **options)
1440
1575
  td = create_table_definition(table_name)
1441
- cd = td.new_column_definition(column_name, type, options)
1576
+ cd = td.new_column_definition(column_name, type, **options)
1442
1577
  schema_creation.accept(AddColumnDefinition.new(cd))
1443
1578
  end
1444
1579
 
1445
- def remove_column_for_alter(table_name, column_name, type = nil, options = {})
1580
+ def rename_column_sql(table_name, column_name, new_column_name)
1581
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1582
+ end
1583
+
1584
+ def remove_column_for_alter(table_name, column_name, type = nil, **options)
1446
1585
  "DROP COLUMN #{quote_column_name(column_name)}"
1447
1586
  end
1448
1587
 
1449
- def remove_columns_for_alter(table_name, *column_names)
1588
+ def remove_columns_for_alter(table_name, *column_names, **options)
1450
1589
  column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1451
1590
  end
1452
1591
 
1592
+ def add_timestamps_for_alter(table_name, **options)
1593
+ options[:null] = false if options[:null].nil?
1594
+
1595
+ if !options.key?(:precision) && supports_datetime_with_precision?
1596
+ options[:precision] = 6
1597
+ end
1598
+
1599
+ [
1600
+ add_column_for_alter(table_name, :created_at, :datetime, **options),
1601
+ add_column_for_alter(table_name, :updated_at, :datetime, **options)
1602
+ ]
1603
+ end
1604
+
1605
+ def remove_timestamps_for_alter(table_name, **options)
1606
+ remove_columns_for_alter(table_name, :updated_at, :created_at)
1607
+ end
1608
+
1453
1609
  def insert_versions_sql(versions)
1454
1610
  sm_table = quote_table_name(schema_migration.table_name)
1455
1611