activerecord 5.2.8.1 → 6.1.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1347 -624
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +16 -7
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +107 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  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 +1 -2
  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 +3 -4
  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 +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +73 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +225 -121
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +341 -99
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +4 -4
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +113 -74
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +478 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +94 -10
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +291 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +118 -32
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -1,6 +1,5 @@
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
4
  require "digest/sha2"
6
5
 
@@ -97,10 +96,14 @@ module ActiveRecord
97
96
  # # Check an index with a custom name exists
98
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
99
98
  #
100
- def index_exists?(table_name, column_name, options = {})
101
- column_names = Array(column_name).map(&:to_s)
99
+ def index_exists?(table_name, column_name, **options)
102
100
  checks = []
103
- 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
+
104
107
  checks << lambda { |i| i.unique } if options[:unique]
105
108
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
106
109
 
@@ -129,11 +132,11 @@ module ActiveRecord
129
132
  # column_exists?(:suppliers, :name, :string, null: false)
130
133
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
131
134
  #
132
- def column_exists?(table_name, column_name, type = nil, options = {})
135
+ def column_exists?(table_name, column_name, type = nil, **options)
133
136
  column_name = column_name.to_s
134
137
  checks = []
135
138
  checks << lambda { |c| c.name == column_name }
136
- checks << lambda { |c| c.type == type } if type
139
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
137
140
  column_options_keys.each do |attr|
138
141
  checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
139
142
  end
@@ -205,19 +208,22 @@ module ActiveRecord
205
208
  # Set to true to drop the table before creating it.
206
209
  # Set to +:cascade+ to drop dependent objects as well.
207
210
  # Defaults to false.
211
+ # [<tt>:if_not_exists</tt>]
212
+ # Set to true to avoid raising an error when the table already exists.
213
+ # Defaults to false.
208
214
  # [<tt>:as</tt>]
209
215
  # SQL to use to generate the table. When this option is used, the block is
210
216
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
211
217
  #
212
218
  # ====== Add a backend specific option to the generated SQL (MySQL)
213
219
  #
214
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
220
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
215
221
  #
216
222
  # generates:
217
223
  #
218
224
  # CREATE TABLE suppliers (
219
225
  # id bigint auto_increment PRIMARY KEY
220
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
226
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
221
227
  #
222
228
  # ====== Rename the primary key column
223
229
  #
@@ -287,37 +293,44 @@ module ActiveRecord
287
293
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
288
294
  #
289
295
  # See also TableDefinition#column for details on how to create columns.
290
- def create_table(table_name, comment: nil, **options)
291
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
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))
292
298
 
293
- if options[:id] != false && !options[:as]
294
- pk = options.fetch(:primary_key) do
295
- Base.get_primary_key table_name.to_s.singularize
299
+ if id && !td.as
300
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
301
+
302
+ if id.is_a?(Hash)
303
+ options.merge!(id.except(:type))
304
+ id = id.fetch(:type, :primary_key)
296
305
  end
297
306
 
298
307
  if pk.is_a?(Array)
299
308
  td.primary_keys pk
300
309
  else
301
- td.primary_key pk, options.fetch(:id, :primary_key), options
310
+ td.primary_key pk, id, **options
302
311
  end
303
312
  end
304
313
 
305
314
  yield td if block_given?
306
315
 
307
- if options[:force]
308
- 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)
309
320
  end
310
321
 
311
322
  result = execute schema_creation.accept td
312
323
 
313
324
  unless supports_indexes_in_create?
314
325
  td.indexes.each do |column_name, index_options|
315
- add_index(table_name, column_name, index_options)
326
+ add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
316
327
  end
317
328
  end
318
329
 
319
330
  if supports_comments? && !supports_comments_in_create?
320
- change_table_comment(table_name, comment) if comment.present?
331
+ if table_comment = td.comment.presence
332
+ change_table_comment(table_name, table_comment)
333
+ end
321
334
 
322
335
  td.columns.each do |column|
323
336
  change_column_comment(table_name, column.name, column.comment) if column.comment.present?
@@ -372,9 +385,9 @@ module ActiveRecord
372
385
 
373
386
  t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
374
387
 
375
- create_table(join_table_name, options.merge!(id: false)) do |td|
376
- td.references t1_ref, column_options
377
- 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
378
391
  yield td if block_given?
379
392
  end
380
393
  end
@@ -385,7 +398,7 @@ module ActiveRecord
385
398
  # Although this command ignores the block if one is given, it can be helpful
386
399
  # to provide one in a migration's +change+ method so it can be reverted.
387
400
  # In that case, the block will be used by #create_join_table.
388
- def drop_join_table(table_1, table_2, options = {})
401
+ def drop_join_table(table_1, table_2, **options)
389
402
  join_table_name = find_join_table_name(table_1, table_2, options)
390
403
  drop_table(join_table_name)
391
404
  end
@@ -414,6 +427,12 @@ module ActiveRecord
414
427
  # t.column :name, :string, limit: 60
415
428
  # end
416
429
  #
430
+ # ====== Change type of a column
431
+ #
432
+ # change_table(:suppliers) do |t|
433
+ # t.change :metadata, :json
434
+ # end
435
+ #
417
436
  # ====== Add 2 integer columns
418
437
  #
419
438
  # change_table(:suppliers) do |t|
@@ -462,7 +481,7 @@ module ActiveRecord
462
481
  # end
463
482
  #
464
483
  # See also Table for details on all of the various column transformations.
465
- def change_table(table_name, options = {})
484
+ def change_table(table_name, **options)
466
485
  if supports_bulk_alter? && options[:bulk]
467
486
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
468
487
  yield update_table_definition(table_name, recorder)
@@ -492,7 +511,8 @@ module ActiveRecord
492
511
  # Although this command ignores most +options+ and the block if one is given,
493
512
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
494
513
  # In that case, +options+ and the block will be used by #create_table.
495
- def drop_table(table_name, options = {})
514
+ def drop_table(table_name, **options)
515
+ schema_cache.clear_data_source_cache!(table_name.to_s)
496
516
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
497
517
  end
498
518
 
@@ -512,18 +532,25 @@ module ActiveRecord
512
532
  # Available options are (none of these exists by default):
513
533
  # * <tt>:limit</tt> -
514
534
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
515
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
535
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
516
536
  # This option is ignored by some backends.
517
537
  # * <tt>:default</tt> -
518
538
  # The column's default value. Use +nil+ for +NULL+.
519
539
  # * <tt>:null</tt> -
520
540
  # Allows or disallows +NULL+ values in the column.
521
541
  # * <tt>:precision</tt> -
522
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
542
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
543
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
523
544
  # * <tt>:scale</tt> -
524
545
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
546
+ # * <tt>:collation</tt> -
547
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
548
+ # column will have the same collation as the table.
525
549
  # * <tt>:comment</tt> -
526
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.
527
554
  #
528
555
  # Note: The precision is the total number of significant digits,
529
556
  # and the scale is the number of digits that can be stored following
@@ -544,8 +571,6 @@ module ActiveRecord
544
571
  # but the maximum supported <tt>:precision</tt> is 16. No default.
545
572
  # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
546
573
  # Default is (38,0).
547
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
548
- # Default unknown.
549
574
  # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
550
575
  # Default (38,0).
551
576
  #
@@ -575,20 +600,37 @@ module ActiveRecord
575
600
  # # Defines a column with a database-specific type.
576
601
  # add_column(:shapes, :triangle, 'polygon')
577
602
  # # ALTER TABLE "shapes" ADD "triangle" polygon
578
- def add_column(table_name, column_name, type, options = {})
603
+ #
604
+ # # Ignores the method call if the column exists
605
+ # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
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
+
579
609
  at = create_alter_table table_name
580
- at.add_column(column_name, type, options)
610
+ at.add_column(column_name, type, **options)
581
611
  execute schema_creation.accept at
582
612
  end
583
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
+
584
620
  # Removes the given columns from the table definition.
585
621
  #
586
622
  # remove_columns(:suppliers, :qualification, :experience)
587
623
  #
588
- def remove_columns(table_name, *column_names)
589
- 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
+
590
632
  column_names.each do |column_name|
591
- remove_column(table_name, column_name)
633
+ remove_column(table_name, column_name, type, **options)
592
634
  end
593
635
  end
594
636
 
@@ -599,8 +641,17 @@ module ActiveRecord
599
641
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
600
642
  # to provide these in a migration's +change+ method so it can be reverted.
601
643
  # In that case, +type+ and +options+ will be used by #add_column.
602
- def remove_column(table_name, column_name, type = nil, options = {})
603
- execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
644
+ # Indexes on the column are automatically removed.
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)}"
604
655
  end
605
656
 
606
657
  # Changes the column's definition according to the new options.
@@ -609,7 +660,7 @@ module ActiveRecord
609
660
  # change_column(:suppliers, :name, :string, limit: 80)
610
661
  # change_column(:accounts, :description, :text)
611
662
  #
612
- def change_column(table_name, column_name, type, options = {})
663
+ def change_column(table_name, column_name, type, **options)
613
664
  raise NotImplementedError, "change_column is not implemented"
614
665
  end
615
666
 
@@ -671,7 +722,17 @@ module ActiveRecord
671
722
  #
672
723
  # generates:
673
724
  #
674
- # 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.
675
736
  #
676
737
  # ====== Creating a unique index
677
738
  #
@@ -679,7 +740,7 @@ module ActiveRecord
679
740
  #
680
741
  # generates:
681
742
  #
682
- # 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)
683
744
  #
684
745
  # ====== Creating a named index
685
746
  #
@@ -709,7 +770,7 @@ module ActiveRecord
709
770
  #
710
771
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
711
772
  #
712
- # 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})
713
774
  #
714
775
  # generates:
715
776
  #
@@ -725,7 +786,7 @@ module ActiveRecord
725
786
  #
726
787
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
727
788
  #
728
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
789
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
729
790
  #
730
791
  # ====== Creating an index with a specific method
731
792
  #
@@ -760,9 +821,22 @@ module ActiveRecord
760
821
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
761
822
  #
762
823
  # Note: only supported by MySQL.
763
- def add_index(table_name, column_name, options = {})
764
- index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
765
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
824
+ #
825
+ # ====== Creating an index with a specific algorithm
826
+ #
827
+ # add_index(:developers, :name, algorithm: :concurrently)
828
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
829
+ #
830
+ # Note: only supported by PostgreSQL.
831
+ #
832
+ # Concurrently adding an index is not supported in a transaction.
833
+ #
834
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
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)
766
840
  end
767
841
 
768
842
  # Removes the given index from the table.
@@ -783,8 +857,29 @@ module ActiveRecord
783
857
  #
784
858
  # remove_index :accounts, name: :by_branch_party
785
859
  #
786
- def remove_index(table_name, options = {})
787
- index_name = index_name_for_remove(table_name, options)
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
+ #
869
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
870
+ #
871
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
872
+ #
873
+ # Note: only supported by PostgreSQL.
874
+ #
875
+ # Concurrently removing an index is not supported in a transaction.
876
+ #
877
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
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
+
788
883
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
789
884
  end
790
885
 
@@ -795,6 +890,8 @@ module ActiveRecord
795
890
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
796
891
  #
797
892
  def rename_index(table_name, old_name, new_name)
893
+ old_name = old_name.to_s
894
+ new_name = new_name.to_s
798
895
  validate_index_length!(table_name, new_name)
799
896
 
800
897
  # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
@@ -836,23 +933,25 @@ module ActiveRecord
836
933
  # Add an appropriate index. Defaults to true.
837
934
  # See #add_index for usage of this option.
838
935
  # [<tt>:foreign_key</tt>]
839
- # 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.
840
939
  # [<tt>:polymorphic</tt>]
841
940
  # Whether an additional +_type+ column should be added. Defaults to false.
842
941
  # [<tt>:null</tt>]
843
942
  # Whether the column allows nulls. Defaults to true.
844
943
  #
845
- # ====== Create a user_id bigint column
944
+ # ====== Create a user_id bigint column without an index
846
945
  #
847
- # add_reference(:products, :user)
946
+ # add_reference(:products, :user, index: false)
848
947
  #
849
948
  # ====== Create a user_id string column
850
949
  #
851
950
  # add_reference(:products, :user, type: :string)
852
951
  #
853
- # ====== Create supplier_id, supplier_type columns and appropriate index
952
+ # ====== Create supplier_id, supplier_type columns
854
953
  #
855
- # add_reference(:products, :supplier, polymorphic: true, index: true)
954
+ # add_reference(:products, :supplier, polymorphic: true)
856
955
  #
857
956
  # ====== Create a supplier_id column with a unique index
858
957
  #
@@ -868,10 +967,10 @@ module ActiveRecord
868
967
  #
869
968
  # ====== Create a supplier_id column and a foreign key to the firms table
870
969
  #
871
- # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
970
+ # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
872
971
  #
873
972
  def add_reference(table_name, ref_name, **options)
874
- 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))
875
974
  end
876
975
  alias :add_belongs_to :add_reference
877
976
 
@@ -880,7 +979,7 @@ module ActiveRecord
880
979
  #
881
980
  # ====== Remove the reference
882
981
  #
883
- # remove_reference(:products, :user, index: true)
982
+ # remove_reference(:products, :user, index: false)
884
983
  #
885
984
  # ====== Remove polymorphic reference
886
985
  #
@@ -888,7 +987,7 @@ module ActiveRecord
888
987
  #
889
988
  # ====== Remove the reference with a foreign key
890
989
  #
891
- # remove_reference(:products, :user, index: true, foreign_key: true)
990
+ # remove_reference(:products, :user, foreign_key: true)
892
991
  #
893
992
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
894
993
  if foreign_key
@@ -899,7 +998,7 @@ module ActiveRecord
899
998
  foreign_key_options = { to_table: reference_name }
900
999
  end
901
1000
  foreign_key_options[:column] ||= "#{ref_name}_id"
902
- remove_foreign_key(table_name, foreign_key_options)
1001
+ remove_foreign_key(table_name, **foreign_key_options)
903
1002
  end
904
1003
 
905
1004
  remove_column(table_name, "#{ref_name}_id")
@@ -956,8 +1055,8 @@ module ActiveRecord
956
1055
  # [<tt>:on_update</tt>]
957
1056
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
958
1057
  # [<tt>:validate</tt>]
959
- # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
960
- def add_foreign_key(from_table, to_table, options = {})
1058
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1059
+ def add_foreign_key(from_table, to_table, **options)
961
1060
  return unless supports_foreign_keys?
962
1061
 
963
1062
  options = foreign_key_options(from_table, to_table, options)
@@ -980,15 +1079,22 @@ module ActiveRecord
980
1079
  #
981
1080
  # remove_foreign_key :accounts, column: :owner_id
982
1081
  #
1082
+ # Removes the foreign key on +accounts.owner_id+.
1083
+ #
1084
+ # remove_foreign_key :accounts, to_table: :owners
1085
+ #
983
1086
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
984
1087
  #
985
1088
  # remove_foreign_key :accounts, name: :special_fk_name
986
1089
  #
987
- # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
988
- def remove_foreign_key(from_table, options_or_to_table = {})
1090
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1091
+ # with an addition of
1092
+ # [<tt>:to_table</tt>]
1093
+ # The name of the table that contains the referenced primary key.
1094
+ def remove_foreign_key(from_table, to_table = nil, **options)
989
1095
  return unless supports_foreign_keys?
990
1096
 
991
- fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
1097
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
992
1098
 
993
1099
  at = create_alter_table from_table
994
1100
  at.drop_foreign_key fk_name_to_delete
@@ -1007,14 +1113,12 @@ module ActiveRecord
1007
1113
  # # Checks to see if a foreign key with a custom name exists.
1008
1114
  # foreign_key_exists?(:accounts, name: "special_fk_name")
1009
1115
  #
1010
- def foreign_key_exists?(from_table, options_or_to_table = {})
1011
- foreign_key_for(from_table, options_or_to_table).present?
1116
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1117
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1012
1118
  end
1013
1119
 
1014
1120
  def foreign_key_column_for(table_name) # :nodoc:
1015
- prefix = Base.table_name_prefix
1016
- suffix = Base.table_name_suffix
1017
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1121
+ name = strip_table_name_prefix_and_suffix(table_name)
1018
1122
  "#{name.singularize}_id"
1019
1123
  end
1020
1124
 
@@ -1025,8 +1129,62 @@ module ActiveRecord
1025
1129
  options
1026
1130
  end
1027
1131
 
1028
- def dump_schema_information #:nodoc:
1029
- versions = ActiveRecord::SchemaMigration.all_versions
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
+
1186
+ def dump_schema_information # :nodoc:
1187
+ versions = schema_migration.all_versions
1030
1188
  insert_versions_sql(versions) if versions.any?
1031
1189
  end
1032
1190
 
@@ -1034,15 +1192,12 @@ module ActiveRecord
1034
1192
  { primary_key: true }
1035
1193
  end
1036
1194
 
1037
- def assume_migrated_upto_version(version, migrations_paths)
1038
- migrations_paths = Array(migrations_paths)
1195
+ def assume_migrated_upto_version(version)
1039
1196
  version = version.to_i
1040
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1197
+ sm_table = quote_table_name(schema_migration.table_name)
1041
1198
 
1042
- migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
1043
- versions = migration_context.migration_files.map do |file|
1044
- migration_context.parse_migration_filename(file).first.to_i
1045
- end
1199
+ migrated = migration_context.get_all_versions
1200
+ versions = migration_context.migrations.map(&:version)
1046
1201
 
1047
1202
  unless migrated.include?(version)
1048
1203
  execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
@@ -1053,13 +1208,7 @@ module ActiveRecord
1053
1208
  if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1054
1209
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1055
1210
  end
1056
- if supports_multi_insert?
1057
- execute insert_versions_sql(inserting)
1058
- else
1059
- inserting.each do |v|
1060
- execute insert_versions_sql(v)
1061
- end
1062
- end
1211
+ execute insert_versions_sql(inserting)
1063
1212
  end
1064
1213
  end
1065
1214
 
@@ -1085,7 +1234,7 @@ module ActiveRecord
1085
1234
  if (0..6) === precision
1086
1235
  column_type_sql << "(#{precision})"
1087
1236
  else
1088
- raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1237
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1089
1238
  end
1090
1239
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1091
1240
  column_type_sql << "(#{limit})"
@@ -1112,18 +1261,22 @@ module ActiveRecord
1112
1261
  #
1113
1262
  # add_timestamps(:suppliers, null: true)
1114
1263
  #
1115
- def add_timestamps(table_name, options = {})
1264
+ def add_timestamps(table_name, **options)
1116
1265
  options[:null] = false if options[:null].nil?
1117
1266
 
1118
- add_column table_name, :created_at, :datetime, options
1119
- add_column table_name, :updated_at, :datetime, options
1267
+ if !options.key?(:precision) && supports_datetime_with_precision?
1268
+ options[:precision] = 6
1269
+ end
1270
+
1271
+ add_column table_name, :created_at, :datetime, **options
1272
+ add_column table_name, :updated_at, :datetime, **options
1120
1273
  end
1121
1274
 
1122
1275
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1123
1276
  #
1124
1277
  # remove_timestamps(:suppliers)
1125
1278
  #
1126
- def remove_timestamps(table_name, options = {})
1279
+ def remove_timestamps(table_name, **options)
1127
1280
  remove_column table_name, :updated_at
1128
1281
  remove_column table_name, :created_at
1129
1282
  end
@@ -1132,36 +1285,43 @@ module ActiveRecord
1132
1285
  Table.new(table_name, base)
1133
1286
  end
1134
1287
 
1135
- def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1136
- 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)
1137
1290
 
1138
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
1291
+ column_names = index_column_names(column_name)
1139
1292
 
1140
- index_type = options[:type].to_s if options.key?(:type)
1141
- index_type ||= options[:unique] ? "UNIQUE" : ""
1142
- index_name = options[:name].to_s if options.key?(:name)
1293
+ index_name = name&.to_s
1143
1294
  index_name ||= index_name(table_name, column_names)
1144
1295
 
1145
- if options.key?(:algorithm)
1146
- algorithm = index_algorithms.fetch(options[:algorithm]) {
1147
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
1148
- }
1149
- end
1150
-
1151
- using = "USING #{options[:using]}" if options[:using].present?
1152
-
1153
- if supports_partial_index?
1154
- index_options = options[:where] ? " WHERE #{options[:where]}" : ""
1155
- 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
1156
1313
 
1157
- 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
1158
1319
 
1159
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
1160
- 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
1161
1323
  end
1162
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
1163
-
1164
- [index_name, index_type, index_columns, index_options, algorithm, using, comment]
1324
+ add_options_for_index_columns(quoted_columns, **options).values.join(", ")
1165
1325
  end
1166
1326
 
1167
1327
  def options_include_default?(options)
@@ -1169,12 +1329,22 @@ module ActiveRecord
1169
1329
  end
1170
1330
 
1171
1331
  # Changes the comment for a table or removes it if +nil+.
1172
- def change_table_comment(table_name, comment)
1332
+ #
1333
+ # Passing a hash containing +:from+ and +:to+ will make this change
1334
+ # reversible in migration:
1335
+ #
1336
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1337
+ def change_table_comment(table_name, comment_or_changes)
1173
1338
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1174
1339
  end
1175
1340
 
1176
1341
  # Changes the comment for a column or removes it if +nil+.
1177
- def change_column_comment(table_name, column_name, comment)
1342
+ #
1343
+ # Passing a hash containing +:from+ and +:to+ will make this change
1344
+ # reversible in migration:
1345
+ #
1346
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1347
+ def change_column_comment(table_name, column_name, comment_or_changes)
1178
1348
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1179
1349
  end
1180
1350
 
@@ -1206,31 +1376,26 @@ module ActiveRecord
1206
1376
  # the PostgreSQL adapter for supporting operator classes.
1207
1377
  def add_options_for_index_columns(quoted_columns, **options)
1208
1378
  if supports_index_sort_order?
1209
- quoted_columns = add_index_sort_order(quoted_columns, options)
1379
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
1210
1380
  end
1211
1381
 
1212
1382
  quoted_columns
1213
1383
  end
1214
1384
 
1215
- def quoted_columns_for_index(column_names, **options)
1216
- return [column_names] if column_names.is_a?(String)
1217
-
1218
- quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1219
- add_options_for_index_columns(quoted_columns, options).values
1220
- end
1221
-
1222
- def index_name_for_remove(table_name, options = {})
1223
- 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)
1224
1387
 
1225
1388
  checks = []
1226
1389
 
1227
- if options.is_a?(Hash)
1228
- checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1229
- column_names = index_column_names(options[:column])
1390
+ if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
1391
+ options[:name] = index_name(table_name, column_name)
1392
+ column_names = []
1230
1393
  else
1231
- column_names = index_column_names(options)
1394
+ column_names = index_column_names(column_name || options[:column])
1232
1395
  end
1233
1396
 
1397
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1398
+
1234
1399
  if column_names.present?
1235
1400
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1236
1401
  end
@@ -1275,14 +1440,18 @@ module ActiveRecord
1275
1440
  SchemaCreation.new(self)
1276
1441
  end
1277
1442
 
1278
- def create_table_definition(*args)
1279
- TableDefinition.new(*args)
1443
+ def create_table_definition(name, **options)
1444
+ TableDefinition.new(self, name, **options)
1280
1445
  end
1281
1446
 
1282
1447
  def create_alter_table(name)
1283
1448
  AlterTable.new create_table_definition(name)
1284
1449
  end
1285
1450
 
1451
+ def extract_table_options!(options)
1452
+ options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1453
+ end
1454
+
1286
1455
  def fetch_type_metadata(sql_type)
1287
1456
  cast_type = lookup_cast_type(sql_type)
1288
1457
  SqlTypeMetadata.new(
@@ -1310,6 +1479,12 @@ module ActiveRecord
1310
1479
  { column: column_names }
1311
1480
  end
1312
1481
 
1482
+ def strip_table_name_prefix_and_suffix(table_name)
1483
+ prefix = Base.table_name_prefix
1484
+ suffix = Base.table_name_suffix
1485
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1486
+ end
1487
+
1313
1488
  def foreign_key_name(table_name, options)
1314
1489
  options.fetch(:name) do
1315
1490
  identifier = "#{table_name}_#{options.fetch(:column)}_fk"
@@ -1319,14 +1494,14 @@ module ActiveRecord
1319
1494
  end
1320
1495
  end
1321
1496
 
1322
- def foreign_key_for(from_table, options_or_to_table = {})
1497
+ def foreign_key_for(from_table, **options)
1323
1498
  return unless supports_foreign_keys?
1324
- foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
1499
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1325
1500
  end
1326
1501
 
1327
- def foreign_key_for!(from_table, options_or_to_table = {})
1328
- foreign_key_for(from_table, options_or_to_table) || \
1329
- raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
1502
+ def foreign_key_for!(from_table, to_table: nil, **options)
1503
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1504
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1330
1505
  end
1331
1506
 
1332
1507
  def extract_foreign_key_action(specifier)
@@ -1337,11 +1512,30 @@ module ActiveRecord
1337
1512
  end
1338
1513
  end
1339
1514
 
1340
- def validate_index_length!(table_name, new_name, internal = false)
1341
- max_index_length = internal ? index_name_length : allowed_index_name_length
1515
+ def check_constraint_name(table_name, **options)
1516
+ options.fetch(:name) do
1517
+ expression = options.fetch(:expression)
1518
+ identifier = "#{table_name}_#{expression}_chk"
1519
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1342
1520
 
1343
- if new_name.length > max_index_length
1344
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1521
+ "chk_rails_#{hashed_identifier}"
1522
+ end
1523
+ end
1524
+
1525
+ def check_constraint_for(table_name, **options)
1526
+ return unless supports_check_constraints?
1527
+ chk_name = check_constraint_name(table_name, **options)
1528
+ check_constraints(table_name).detect { |chk| chk.name == chk_name }
1529
+ end
1530
+
1531
+ def check_constraint_for!(table_name, expression: nil, **options)
1532
+ check_constraint_for(table_name, expression: expression, **options) ||
1533
+ raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
1534
+ end
1535
+
1536
+ def validate_index_length!(table_name, new_name, internal = false)
1537
+ if new_name.length > index_name_length
1538
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
1345
1539
  end
1346
1540
  end
1347
1541
 
@@ -1352,30 +1546,77 @@ module ActiveRecord
1352
1546
  default_or_changes
1353
1547
  end
1354
1548
  end
1549
+ alias :extract_new_comment_value :extract_new_default_value
1355
1550
 
1356
- def can_remove_index_by_name?(options)
1357
- options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1551
+ def can_remove_index_by_name?(column_name, options)
1552
+ column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1358
1553
  end
1359
1554
 
1360
- def add_column_for_alter(table_name, column_name, type, options = {})
1555
+ def bulk_change_table(table_name, operations)
1556
+ sql_fragments = []
1557
+ non_combinable_operations = []
1558
+
1559
+ operations.each do |command, args|
1560
+ table, arguments = args.shift, args
1561
+ method = :"#{command}_for_alter"
1562
+
1563
+ if respond_to?(method, true)
1564
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1565
+ sql_fragments << sqls
1566
+ non_combinable_operations.concat(procs)
1567
+ else
1568
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1569
+ non_combinable_operations.each(&:call)
1570
+ sql_fragments = []
1571
+ non_combinable_operations = []
1572
+ send(command, table, *arguments)
1573
+ end
1574
+ end
1575
+
1576
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1577
+ non_combinable_operations.each(&:call)
1578
+ end
1579
+
1580
+ def add_column_for_alter(table_name, column_name, type, **options)
1361
1581
  td = create_table_definition(table_name)
1362
- cd = td.new_column_definition(column_name, type, options)
1582
+ cd = td.new_column_definition(column_name, type, **options)
1363
1583
  schema_creation.accept(AddColumnDefinition.new(cd))
1364
1584
  end
1365
1585
 
1366
- def remove_column_for_alter(table_name, column_name, type = nil, options = {})
1586
+ def rename_column_sql(table_name, column_name, new_column_name)
1587
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1588
+ end
1589
+
1590
+ def remove_column_for_alter(table_name, column_name, type = nil, **options)
1367
1591
  "DROP COLUMN #{quote_column_name(column_name)}"
1368
1592
  end
1369
1593
 
1370
- def remove_columns_for_alter(table_name, *column_names)
1594
+ def remove_columns_for_alter(table_name, *column_names, **options)
1371
1595
  column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1372
1596
  end
1373
1597
 
1598
+ def add_timestamps_for_alter(table_name, **options)
1599
+ options[:null] = false if options[:null].nil?
1600
+
1601
+ if !options.key?(:precision) && supports_datetime_with_precision?
1602
+ options[:precision] = 6
1603
+ end
1604
+
1605
+ [
1606
+ add_column_for_alter(table_name, :created_at, :datetime, **options),
1607
+ add_column_for_alter(table_name, :updated_at, :datetime, **options)
1608
+ ]
1609
+ end
1610
+
1611
+ def remove_timestamps_for_alter(table_name, **options)
1612
+ remove_columns_for_alter(table_name, :updated_at, :created_at)
1613
+ end
1614
+
1374
1615
  def insert_versions_sql(versions)
1375
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1616
+ sm_table = quote_table_name(schema_migration.table_name)
1376
1617
 
1377
1618
  if versions.is_a?(Array)
1378
- sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
1619
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1379
1620
  sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1380
1621
  sql << ";\n\n"
1381
1622
  sql