activerecord 5.2.8.1 → 6.1.6.1

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 (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  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 +1 -2
  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 +98 -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 +47 -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 +224 -120
  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 +333 -98
  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 +3 -3
  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 +116 -59
  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 +476 -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 +88 -9
  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 +287 -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 +116 -30
  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