activerecord 5.0.7.2 → 6.1.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 (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,105 +1,93 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters # :nodoc:
3
- # The goal of this module is to move Adapter specific column
4
- # definitions to the Adapter instead of having it in the schema
5
- # dumper itself. This code represents the normal case.
6
- # We can then redefine how certain data types may be handled in the schema dumper on the
7
- # Adapter level by over-writing this code inside the database specific adapters
8
- module ColumnDumper
9
- def column_spec(column)
10
- spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
11
- spec[:name] = column.name.inspect
12
- spec[:type] = schema_type(column).to_s
13
- spec
14
- end
15
-
16
- def column_spec_for_primary_key(column)
17
- return {} if default_primary_key?(column)
18
- spec = { id: schema_type(column).inspect }
19
- spec.merge!(prepare_column_options(column).except!(:null))
5
+ class SchemaDumper < SchemaDumper # :nodoc:
6
+ def self.create(connection, options)
7
+ new(connection, options)
20
8
  end
21
9
 
22
- # This can be overridden on an Adapter level basis to support other
23
- # extended datatypes (Example: Adding an array option in the
24
- # PostgreSQL::ColumnDumper)
25
- def prepare_column_options(column)
26
- spec = {}
27
-
28
- if limit = schema_limit(column)
29
- spec[:limit] = limit
10
+ private
11
+ def column_spec(column)
12
+ [schema_type_with_virtual(column), prepare_column_options(column)]
30
13
  end
31
14
 
32
- if precision = schema_precision(column)
33
- spec[:precision] = precision
15
+ def column_spec_for_primary_key(column)
16
+ spec = {}
17
+ spec[:id] = schema_type(column).inspect unless default_primary_key?(column)
18
+ spec.merge!(prepare_column_options(column).except!(:null))
19
+ spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
+ spec
34
21
  end
35
22
 
36
- if scale = schema_scale(column)
37
- spec[:scale] = scale
23
+ def prepare_column_options(column)
24
+ spec = {}
25
+ spec[:limit] = schema_limit(column)
26
+ spec[:precision] = schema_precision(column)
27
+ spec[:scale] = schema_scale(column)
28
+ spec[:default] = schema_default(column)
29
+ spec[:null] = "false" unless column.null
30
+ spec[:collation] = schema_collation(column)
31
+ spec[:comment] = column.comment.inspect if column.comment.present?
32
+ spec.compact!
33
+ spec
38
34
  end
39
35
 
40
- default = schema_default(column) if column.has_default?
41
- spec[:default] = default unless default.nil?
42
-
43
- spec[:null] = 'false' unless column.null
44
-
45
- if collation = schema_collation(column)
46
- spec[:collation] = collation
36
+ def default_primary_key?(column)
37
+ schema_type(column) == :bigint
47
38
  end
48
39
 
49
- spec[:comment] = column.comment.inspect if column.comment.present?
50
-
51
- spec
52
- end
53
-
54
- # Lists the valid migration options
55
- def migration_keys
56
- [:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
57
- end
58
-
59
- private
40
+ def explicit_primary_key_default?(column)
41
+ false
42
+ end
60
43
 
61
- def default_primary_key?(column)
62
- schema_type(column) == :integer
63
- end
44
+ def schema_type_with_virtual(column)
45
+ if @connection.supports_virtual_columns? && column.virtual?
46
+ :virtual
47
+ else
48
+ schema_type(column)
49
+ end
50
+ end
64
51
 
65
- def schema_type(column)
66
- if column.bigint?
67
- :bigint
68
- else
69
- column.type
52
+ def schema_type(column)
53
+ if column.bigint?
54
+ :bigint
55
+ else
56
+ column.type
57
+ end
70
58
  end
71
- end
72
59
 
73
- def schema_limit(column)
74
- limit = column.limit unless column.bigint?
75
- limit.inspect if limit && limit != native_database_types[column.type][:limit]
76
- end
60
+ def schema_limit(column)
61
+ limit = column.limit unless column.bigint?
62
+ limit.inspect if limit && limit != @connection.native_database_types[column.type][:limit]
63
+ end
77
64
 
78
- def schema_precision(column)
79
- column.precision.inspect if column.precision
80
- end
65
+ def schema_precision(column)
66
+ column.precision.inspect if column.precision
67
+ end
81
68
 
82
- def schema_scale(column)
83
- column.scale.inspect if column.scale
84
- end
69
+ def schema_scale(column)
70
+ column.scale.inspect if column.scale
71
+ end
85
72
 
86
- def schema_default(column)
87
- type = lookup_cast_type_from_column(column)
88
- default = type.deserialize(column.default)
89
- if default.nil?
90
- schema_expression(column)
91
- else
92
- type.type_cast_for_schema(default)
73
+ def schema_default(column)
74
+ return unless column.has_default?
75
+ type = @connection.lookup_cast_type_from_column(column)
76
+ default = type.deserialize(column.default)
77
+ if default.nil?
78
+ schema_expression(column)
79
+ else
80
+ type.type_cast_for_schema(default)
81
+ end
93
82
  end
94
- end
95
83
 
96
- def schema_expression(column)
97
- "-> { #{column.default_function.inspect} }" if column.default_function
98
- end
84
+ def schema_expression(column)
85
+ "-> { #{column.default_function.inspect} }" if column.default_function
86
+ end
99
87
 
100
- def schema_collation(column)
101
- column.collation.inspect if column.collation
102
- end
88
+ def schema_collation(column)
89
+ column.collation.inspect if column.collation
90
+ end
103
91
  end
104
92
  end
105
93
  end
@@ -1,6 +1,7 @@
1
- require 'active_record/migration/join_table'
2
- require 'active_support/core_ext/string/access'
3
- require 'digest'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/access"
4
+ require "digest/sha2"
4
5
 
5
6
  module ActiveRecord
6
7
  module ConnectionAdapters # :nodoc:
@@ -25,12 +26,14 @@ module ActiveRecord
25
26
 
26
27
  # Truncates a table alias according to the limits of the current adapter.
27
28
  def table_alias_for(table_name)
28
- table_name[0...table_alias_length].tr('.', '_')
29
+ table_name[0...table_alias_length].tr(".", "_")
29
30
  end
30
31
 
31
32
  # Returns the relation names useable to back Active Record models.
32
33
  # For most adapters this means all #tables and #views.
33
34
  def data_sources
35
+ query_values(data_source_sql, "SCHEMA")
36
+ rescue NotImplementedError
34
37
  tables | views
35
38
  end
36
39
 
@@ -39,12 +42,14 @@ module ActiveRecord
39
42
  # data_source_exists?(:ebooks)
40
43
  #
41
44
  def data_source_exists?(name)
45
+ query_values(data_source_sql(name), "SCHEMA").any? if name.present?
46
+ rescue NotImplementedError
42
47
  data_sources.include?(name.to_s)
43
48
  end
44
49
 
45
50
  # Returns an array of table names defined in the database.
46
- def tables(name = nil)
47
- raise NotImplementedError, "#tables is not implemented"
51
+ def tables
52
+ query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
48
53
  end
49
54
 
50
55
  # Checks to see if the table +table_name+ exists on the database.
@@ -52,12 +57,14 @@ module ActiveRecord
52
57
  # table_exists?(:developers)
53
58
  #
54
59
  def table_exists?(table_name)
60
+ query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
61
+ rescue NotImplementedError
55
62
  tables.include?(table_name.to_s)
56
63
  end
57
64
 
58
65
  # Returns an array of view names defined in the database.
59
66
  def views
60
- raise NotImplementedError, "#views is not implemented"
67
+ query_values(data_source_sql(type: "VIEW"), "SCHEMA")
61
68
  end
62
69
 
63
70
  # Checks to see if the view +view_name+ exists on the database.
@@ -65,11 +72,15 @@ module ActiveRecord
65
72
  # view_exists?(:ebooks)
66
73
  #
67
74
  def view_exists?(view_name)
75
+ query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
76
+ rescue NotImplementedError
68
77
  views.include?(view_name.to_s)
69
78
  end
70
79
 
71
80
  # Returns an array of indexes for the given table.
72
- # def indexes(table_name, name = nil) end
81
+ def indexes(table_name)
82
+ raise NotImplementedError, "#indexes is not implemented"
83
+ end
73
84
 
74
85
  # Checks to see if an index exists on a table for a given index definition.
75
86
  #
@@ -85,20 +96,26 @@ module ActiveRecord
85
96
  # # Check an index with a custom name exists
86
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
87
98
  #
88
- def index_exists?(table_name, column_name, options = {})
89
- column_names = Array(column_name).map(&:to_s)
99
+ def index_exists?(table_name, column_name, **options)
90
100
  checks = []
91
- checks << lambda { |i| 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
+
92
107
  checks << lambda { |i| i.unique } if options[:unique]
93
108
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
94
109
 
95
110
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
96
111
  end
97
112
 
98
- # Returns an array of Column objects for the table specified by +table_name+.
99
- # See the concrete implementation for details on the expected parameter values.
113
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
100
114
  def columns(table_name)
101
- raise NotImplementedError, "#columns is not implemented"
115
+ table_name = table_name.to_s
116
+ column_definitions(table_name).map do |field|
117
+ new_column_from_field(table_name, field)
118
+ end
102
119
  end
103
120
 
104
121
  # Checks to see if a column exists in a given table.
@@ -115,12 +132,12 @@ module ActiveRecord
115
132
  # column_exists?(:suppliers, :name, :string, null: false)
116
133
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
117
134
  #
118
- def column_exists?(table_name, column_name, type = nil, options = {})
135
+ def column_exists?(table_name, column_name, type = nil, **options)
119
136
  column_name = column_name.to_s
120
137
  checks = []
121
138
  checks << lambda { |c| c.name == column_name }
122
- checks << lambda { |c| c.type == type } if type
123
- (migration_keys - [:name]).each do |attr|
139
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
140
+ column_options_keys.each do |attr|
124
141
  checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
125
142
  end
126
143
 
@@ -174,7 +191,9 @@ module ActiveRecord
174
191
  # A Symbol can be used to specify the type of the generated primary key column.
175
192
  # [<tt>:primary_key</tt>]
176
193
  # The name of the primary key, if one is to be added automatically.
177
- # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
194
+ # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
195
+ #
196
+ # If an array is passed, a composite primary key will be created.
178
197
  #
179
198
  # Note that Active Record models will automatically detect their
180
199
  # primary key. This can be avoided by using
@@ -189,19 +208,22 @@ module ActiveRecord
189
208
  # Set to true to drop the table before creating it.
190
209
  # Set to +:cascade+ to drop dependent objects as well.
191
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.
192
214
  # [<tt>:as</tt>]
193
215
  # SQL to use to generate the table. When this option is used, the block is
194
216
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
195
217
  #
196
218
  # ====== Add a backend specific option to the generated SQL (MySQL)
197
219
  #
198
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
220
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
199
221
  #
200
222
  # generates:
201
223
  #
202
224
  # CREATE TABLE suppliers (
203
- # id int auto_increment PRIMARY KEY
204
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
225
+ # id bigint auto_increment PRIMARY KEY
226
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
205
227
  #
206
228
  # ====== Rename the primary key column
207
229
  #
@@ -212,7 +234,7 @@ module ActiveRecord
212
234
  # generates:
213
235
  #
214
236
  # CREATE TABLE objects (
215
- # guid int auto_increment PRIMARY KEY,
237
+ # guid bigint auto_increment PRIMARY KEY,
216
238
  # name varchar(80)
217
239
  # )
218
240
  #
@@ -229,18 +251,35 @@ module ActiveRecord
229
251
  # label varchar
230
252
  # )
231
253
  #
254
+ # ====== Create a composite primary key
255
+ #
256
+ # create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
257
+ # t.belongs_to :product
258
+ # t.belongs_to :client
259
+ # end
260
+ #
261
+ # generates:
262
+ #
263
+ # CREATE TABLE order (
264
+ # product_id bigint NOT NULL,
265
+ # client_id bigint NOT NULL
266
+ # );
267
+ #
268
+ # ALTER TABLE ONLY "orders"
269
+ # ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
270
+ #
232
271
  # ====== Do not add a primary key column
233
272
  #
234
273
  # create_table(:categories_suppliers, id: false) do |t|
235
- # t.column :category_id, :integer
236
- # t.column :supplier_id, :integer
274
+ # t.column :category_id, :bigint
275
+ # t.column :supplier_id, :bigint
237
276
  # end
238
277
  #
239
278
  # generates:
240
279
  #
241
280
  # CREATE TABLE categories_suppliers (
242
- # category_id int,
243
- # supplier_id int
281
+ # category_id bigint,
282
+ # supplier_id bigint
244
283
  # )
245
284
  #
246
285
  # ====== Create a temporary table based on a query
@@ -254,37 +293,44 @@ module ActiveRecord
254
293
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
255
294
  #
256
295
  # See also TableDefinition#column for details on how to create columns.
257
- def create_table(table_name, comment: nil, **options)
258
- 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))
259
298
 
260
- if options[:id] != false && !options[:as]
261
- pk = options.fetch(:primary_key) do
262
- 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)
263
305
  end
264
306
 
265
307
  if pk.is_a?(Array)
266
308
  td.primary_keys pk
267
309
  else
268
- td.primary_key pk, options.fetch(:id, :primary_key), options
310
+ td.primary_key pk, id, **options
269
311
  end
270
312
  end
271
313
 
272
314
  yield td if block_given?
273
315
 
274
- if options[:force] && data_source_exists?(table_name)
275
- drop_table(table_name, options)
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)
276
320
  end
277
321
 
278
322
  result = execute schema_creation.accept td
279
323
 
280
324
  unless supports_indexes_in_create?
281
325
  td.indexes.each do |column_name, index_options|
282
- add_index(table_name, column_name, index_options)
326
+ add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
283
327
  end
284
328
  end
285
329
 
286
330
  if supports_comments? && !supports_comments_in_create?
287
- 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
288
334
 
289
335
  td.columns.each do |column|
290
336
  change_column_comment(table_name, column.name, column.comment) if column.comment.present?
@@ -300,9 +346,9 @@ module ActiveRecord
300
346
  # # Creates a table called 'assemblies_parts' with no id.
301
347
  # create_join_table(:assemblies, :parts)
302
348
  #
303
- # You can pass a +options+ hash can include the following keys:
349
+ # You can pass an +options+ hash which can include the following keys:
304
350
  # [<tt>:table_name</tt>]
305
- # Sets the table name overriding the default
351
+ # Sets the table name, overriding the default.
306
352
  # [<tt>:column_options</tt>]
307
353
  # Any extra options you want appended to the columns definition.
308
354
  # [<tt>:options</tt>]
@@ -328,22 +374,20 @@ module ActiveRecord
328
374
  # generates:
329
375
  #
330
376
  # CREATE TABLE assemblies_parts (
331
- # assembly_id int NOT NULL,
332
- # part_id int NOT NULL,
377
+ # assembly_id bigint NOT NULL,
378
+ # part_id bigint NOT NULL,
333
379
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
334
380
  #
335
- def create_join_table(table_1, table_2, options = {})
381
+ def create_join_table(table_1, table_2, column_options: {}, **options)
336
382
  join_table_name = find_join_table_name(table_1, table_2, options)
337
383
 
338
- column_options = options.delete(:column_options) || {}
339
- column_options.reverse_merge!(null: false)
340
- type = column_options.delete(:type) || :integer
384
+ column_options.reverse_merge!(null: false, index: false)
341
385
 
342
- t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
386
+ t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
343
387
 
344
- create_table(join_table_name, options.merge!(id: false)) do |td|
345
- td.send type, t1_column, column_options
346
- td.send type, t2_column, 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
347
391
  yield td if block_given?
348
392
  end
349
393
  end
@@ -354,7 +398,7 @@ module ActiveRecord
354
398
  # Although this command ignores the block if one is given, it can be helpful
355
399
  # to provide one in a migration's +change+ method so it can be reverted.
356
400
  # In that case, the block will be used by #create_join_table.
357
- def drop_join_table(table_1, table_2, options = {})
401
+ def drop_join_table(table_1, table_2, **options)
358
402
  join_table_name = find_join_table_name(table_1, table_2, options)
359
403
  drop_table(join_table_name)
360
404
  end
@@ -375,12 +419,20 @@ module ActiveRecord
375
419
  #
376
420
  # Defaults to false.
377
421
  #
422
+ # Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
423
+ #
378
424
  # ====== Add a column
379
425
  #
380
426
  # change_table(:suppliers) do |t|
381
427
  # t.column :name, :string, limit: 60
382
428
  # end
383
429
  #
430
+ # ====== Change type of a column
431
+ #
432
+ # change_table(:suppliers) do |t|
433
+ # t.change :metadata, :json
434
+ # end
435
+ #
384
436
  # ====== Add 2 integer columns
385
437
  #
386
438
  # change_table(:suppliers) do |t|
@@ -399,7 +451,7 @@ module ActiveRecord
399
451
  # t.references :company
400
452
  # end
401
453
  #
402
- # Creates a <tt>company_id(integer)</tt> column.
454
+ # Creates a <tt>company_id(bigint)</tt> column.
403
455
  #
404
456
  # ====== Add a polymorphic foreign key column
405
457
  #
@@ -407,7 +459,7 @@ module ActiveRecord
407
459
  # t.belongs_to :company, polymorphic: true
408
460
  # end
409
461
  #
410
- # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
462
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
411
463
  #
412
464
  # ====== Remove a column
413
465
  #
@@ -428,8 +480,8 @@ module ActiveRecord
428
480
  # t.remove_index :company_id
429
481
  # end
430
482
  #
431
- # See also Table for details on all of the various column transformation.
432
- def change_table(table_name, options = {})
483
+ # See also Table for details on all of the various column transformations.
484
+ def change_table(table_name, **options)
433
485
  if supports_bulk_alter? && options[:bulk]
434
486
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
435
487
  yield update_table_definition(table_name, recorder)
@@ -459,7 +511,8 @@ module ActiveRecord
459
511
  # Although this command ignores most +options+ and the block if one is given,
460
512
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
461
513
  # In that case, +options+ and the block will be used by #create_table.
462
- def drop_table(table_name, options = {})
514
+ def drop_table(table_name, **options)
515
+ schema_cache.clear_data_source_cache!(table_name.to_s)
463
516
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
464
517
  end
465
518
 
@@ -478,19 +531,28 @@ module ActiveRecord
478
531
  #
479
532
  # Available options are (none of these exists by default):
480
533
  # * <tt>:limit</tt> -
481
- # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column
482
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
534
+ # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
535
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
536
+ # This option is ignored by some backends.
483
537
  # * <tt>:default</tt> -
484
- # The column's default value. Use nil for NULL.
538
+ # The column's default value. Use +nil+ for +NULL+.
485
539
  # * <tt>:null</tt> -
486
- # Allows or disallows +NULL+ values in the column. This option could
487
- # have been named <tt>:null_allowed</tt>.
540
+ # Allows or disallows +NULL+ values in the column.
488
541
  # * <tt>:precision</tt> -
489
- # 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.
490
544
  # * <tt>:scale</tt> -
491
545
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
492
- #
493
- # Note: The precision is the total number of significant digits
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.
549
+ # * <tt>:comment</tt> -
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.
554
+ #
555
+ # Note: The precision is the total number of significant digits,
494
556
  # and the scale is the number of digits that can be stored following
495
557
  # the decimal point. For example, the number 123.45 has a precision of 5
496
558
  # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
@@ -509,9 +571,7 @@ module ActiveRecord
509
571
  # but the maximum supported <tt>:precision</tt> is 16. No default.
510
572
  # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
511
573
  # Default is (38,0).
512
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
513
- # Default unknown.
514
- # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
574
+ # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
515
575
  # Default (38,0).
516
576
  #
517
577
  # == Examples
@@ -533,23 +593,44 @@ module ActiveRecord
533
593
  # add_column(:measurements, :huge_integer, :decimal, precision: 30)
534
594
  # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
535
595
  #
596
+ # # Defines a column that stores an array of a type.
597
+ # add_column(:users, :skills, :text, array: true)
598
+ # # ALTER TABLE "users" ADD "skills" text[]
599
+ #
536
600
  # # Defines a column with a database-specific type.
537
601
  # add_column(:shapes, :triangle, 'polygon')
538
602
  # # ALTER TABLE "shapes" ADD "triangle" polygon
539
- 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
+
540
609
  at = create_alter_table table_name
541
- at.add_column(column_name, type, options)
610
+ at.add_column(column_name, type, **options)
542
611
  execute schema_creation.accept at
543
612
  end
544
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
+
545
620
  # Removes the given columns from the table definition.
546
621
  #
547
622
  # remove_columns(:suppliers, :qualification, :experience)
548
623
  #
549
- def remove_columns(table_name, *column_names)
550
- 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
+
551
632
  column_names.each do |column_name|
552
- remove_column(table_name, column_name)
633
+ remove_column(table_name, column_name, type, **options)
553
634
  end
554
635
  end
555
636
 
@@ -559,9 +640,18 @@ module ActiveRecord
559
640
  #
560
641
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
561
642
  # to provide these in a migration's +change+ method so it can be reverted.
562
- # In that case, +type+ and +options+ will be used by add_column.
563
- def remove_column(table_name, column_name, type = nil, options = {})
564
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
643
+ # In that case, +type+ and +options+ will be used by #add_column.
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)}"
565
655
  end
566
656
 
567
657
  # Changes the column's definition according to the new options.
@@ -570,7 +660,7 @@ module ActiveRecord
570
660
  # change_column(:suppliers, :name, :string, limit: 80)
571
661
  # change_column(:accounts, :description, :text)
572
662
  #
573
- def change_column(table_name, column_name, type, options = {})
663
+ def change_column(table_name, column_name, type, **options)
574
664
  raise NotImplementedError, "change_column is not implemented"
575
665
  end
576
666
 
@@ -632,7 +722,17 @@ module ActiveRecord
632
722
  #
633
723
  # generates:
634
724
  #
635
- # 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.
636
736
  #
637
737
  # ====== Creating a unique index
638
738
  #
@@ -640,7 +740,7 @@ module ActiveRecord
640
740
  #
641
741
  # generates:
642
742
  #
643
- # 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)
644
744
  #
645
745
  # ====== Creating a named index
646
746
  #
@@ -670,13 +770,13 @@ module ActiveRecord
670
770
  #
671
771
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
672
772
  #
673
- # 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})
674
774
  #
675
775
  # generates:
676
776
  #
677
777
  # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
678
778
  #
679
- # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
779
+ # Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
680
780
  #
681
781
  # ====== Creating a partial index
682
782
  #
@@ -686,7 +786,7 @@ module ActiveRecord
686
786
  #
687
787
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
688
788
  #
689
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
789
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
690
790
  #
691
791
  # ====== Creating an index with a specific method
692
792
  #
@@ -699,6 +799,19 @@ module ActiveRecord
699
799
  #
700
800
  # Note: only supported by PostgreSQL and MySQL
701
801
  #
802
+ # ====== Creating an index with a specific operator class
803
+ #
804
+ # add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
805
+ # # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
806
+ #
807
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
808
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
809
+ #
810
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
811
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
812
+ #
813
+ # Note: only supported by PostgreSQL
814
+ #
702
815
  # ====== Creating an index with a specific type
703
816
  #
704
817
  # add_index(:developers, :name, type: :fulltext)
@@ -708,9 +821,22 @@ module ActiveRecord
708
821
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
709
822
  #
710
823
  # Note: only supported by MySQL.
711
- def add_index(table_name, column_name, options = {})
712
- index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
713
- 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)
714
840
  end
715
841
 
716
842
  # Removes the given index from the table.
@@ -731,8 +857,29 @@ module ActiveRecord
731
857
  #
732
858
  # remove_index :accounts, name: :by_branch_party
733
859
  #
734
- def remove_index(table_name, options = {})
735
- 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
+
736
883
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
737
884
  end
738
885
 
@@ -743,9 +890,11 @@ module ActiveRecord
743
890
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
744
891
  #
745
892
  def rename_index(table_name, old_name, new_name)
893
+ old_name = old_name.to_s
894
+ new_name = new_name.to_s
746
895
  validate_index_length!(table_name, new_name)
747
896
 
748
- # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
897
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
749
898
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
750
899
  return unless old_index_def
751
900
  add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
@@ -767,44 +916,42 @@ module ActiveRecord
767
916
  end
768
917
 
769
918
  # Verifies the existence of an index with a given name.
770
- #
771
- # The default argument is returned if the underlying implementation does not define the indexes method,
772
- # as there's no way to determine the correct answer in that case.
773
- def index_name_exists?(table_name, index_name, default)
774
- return default unless respond_to?(:indexes)
919
+ def index_name_exists?(table_name, index_name)
775
920
  index_name = index_name.to_s
776
921
  indexes(table_name).detect { |i| i.name == index_name }
777
922
  end
778
923
 
779
- # Adds a reference. The reference column is an integer by default,
924
+ # Adds a reference. The reference column is a bigint by default,
780
925
  # the <tt>:type</tt> option can be used to specify a different type.
781
926
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
782
927
  # #add_reference and #add_belongs_to are acceptable.
783
928
  #
784
929
  # The +options+ hash can include the following keys:
785
930
  # [<tt>:type</tt>]
786
- # The reference column type. Defaults to +:integer+.
931
+ # The reference column type. Defaults to +:bigint+.
787
932
  # [<tt>:index</tt>]
788
933
  # Add an appropriate index. Defaults to true.
789
934
  # See #add_index for usage of this option.
790
935
  # [<tt>:foreign_key</tt>]
791
- # 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.
792
939
  # [<tt>:polymorphic</tt>]
793
940
  # Whether an additional +_type+ column should be added. Defaults to false.
794
941
  # [<tt>:null</tt>]
795
942
  # Whether the column allows nulls. Defaults to true.
796
943
  #
797
- # ====== Create a user_id integer column
944
+ # ====== Create a user_id bigint column without an index
798
945
  #
799
- # add_reference(:products, :user)
946
+ # add_reference(:products, :user, index: false)
800
947
  #
801
948
  # ====== Create a user_id string column
802
949
  #
803
950
  # add_reference(:products, :user, type: :string)
804
951
  #
805
- # ====== Create supplier_id, supplier_type columns and appropriate index
952
+ # ====== Create supplier_id, supplier_type columns
806
953
  #
807
- # add_reference(:products, :supplier, polymorphic: true, index: true)
954
+ # add_reference(:products, :supplier, polymorphic: true)
808
955
  #
809
956
  # ====== Create a supplier_id column with a unique index
810
957
  #
@@ -820,10 +967,10 @@ module ActiveRecord
820
967
  #
821
968
  # ====== Create a supplier_id column and a foreign key to the firms table
822
969
  #
823
- # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
970
+ # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
824
971
  #
825
- def add_reference(table_name, *args)
826
- ReferenceDefinition.new(*args).add_to(update_table_definition(table_name, self))
972
+ def add_reference(table_name, ref_name, **options)
973
+ ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
827
974
  end
828
975
  alias :add_belongs_to :add_reference
829
976
 
@@ -832,7 +979,7 @@ module ActiveRecord
832
979
  #
833
980
  # ====== Remove the reference
834
981
  #
835
- # remove_reference(:products, :user, index: true)
982
+ # remove_reference(:products, :user, index: false)
836
983
  #
837
984
  # ====== Remove polymorphic reference
838
985
  #
@@ -840,7 +987,7 @@ module ActiveRecord
840
987
  #
841
988
  # ====== Remove the reference with a foreign key
842
989
  #
843
- # remove_reference(:products, :user, index: true, foreign_key: true)
990
+ # remove_reference(:products, :user, foreign_key: true)
844
991
  #
845
992
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
846
993
  if foreign_key
@@ -850,6 +997,7 @@ module ActiveRecord
850
997
  else
851
998
  foreign_key_options = { to_table: reference_name }
852
999
  end
1000
+ foreign_key_options[:column] ||= "#{ref_name}_id"
853
1001
  remove_foreign_key(table_name, **foreign_key_options)
854
1002
  end
855
1003
 
@@ -906,7 +1054,9 @@ module ActiveRecord
906
1054
  # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
907
1055
  # [<tt>:on_update</tt>]
908
1056
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
909
- def add_foreign_key(from_table, to_table, options = {})
1057
+ # [<tt>:validate</tt>]
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)
910
1060
  return unless supports_foreign_keys?
911
1061
 
912
1062
  options = foreign_key_options(from_table, to_table, options)
@@ -929,15 +1079,22 @@ module ActiveRecord
929
1079
  #
930
1080
  # remove_foreign_key :accounts, column: :owner_id
931
1081
  #
1082
+ # Removes the foreign key on +accounts.owner_id+.
1083
+ #
1084
+ # remove_foreign_key :accounts, to_table: :owners
1085
+ #
932
1086
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
933
1087
  #
934
1088
  # remove_foreign_key :accounts, name: :special_fk_name
935
1089
  #
936
- # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
937
- 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)
938
1095
  return unless supports_foreign_keys?
939
1096
 
940
- 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
941
1098
 
942
1099
  at = create_alter_table from_table
943
1100
  at.drop_foreign_key fk_name_to_delete
@@ -947,33 +1104,21 @@ module ActiveRecord
947
1104
 
948
1105
  # Checks to see if a foreign key exists on a table for a given foreign key definition.
949
1106
  #
950
- # # Check a foreign key exists
1107
+ # # Checks to see if a foreign key exists.
951
1108
  # foreign_key_exists?(:accounts, :branches)
952
1109
  #
953
- # # Check a foreign key on a specified column exists
1110
+ # # Checks to see if a foreign key on a specified column exists.
954
1111
  # foreign_key_exists?(:accounts, column: :owner_id)
955
1112
  #
956
- # # Check a foreign key with a custom name exists
1113
+ # # Checks to see if a foreign key with a custom name exists.
957
1114
  # foreign_key_exists?(:accounts, name: "special_fk_name")
958
1115
  #
959
- def foreign_key_exists?(from_table, options_or_to_table = {})
960
- foreign_key_for(from_table, options_or_to_table).present?
961
- end
962
-
963
- def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
964
- return unless supports_foreign_keys?
965
- foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
966
- end
967
-
968
- def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
969
- foreign_key_for(from_table, options_or_to_table) or \
970
- raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
1116
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1117
+ foreign_key_for(from_table, to_table: to_table, **options).present?
971
1118
  end
972
1119
 
973
1120
  def foreign_key_column_for(table_name) # :nodoc:
974
- prefix = Base.table_name_prefix
975
- suffix = Base.table_name_suffix
976
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1121
+ name = strip_table_name_prefix_and_suffix(table_name)
977
1122
  "#{name.singularize}_id"
978
1123
  end
979
1124
 
@@ -984,68 +1129,90 @@ module ActiveRecord
984
1129
  options
985
1130
  end
986
1131
 
987
- def dump_schema_information #:nodoc:
988
- versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
989
- insert_versions_sql(versions) if versions.any?
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
990
1136
  end
991
1137
 
992
- def insert_versions_sql(versions) # :nodoc:
993
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
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?
994
1154
 
995
- if versions.is_a?(Array)
996
- sql = "INSERT INTO #{sm_table} (version) VALUES\n"
997
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
998
- sql << ";\n\n"
999
- sql
1000
- else
1001
- "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1002
- end
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)
1003
1160
  end
1004
1161
 
1005
- # Should not be called normally, but this operation is non-destructive.
1006
- # The migrations module handles this automatically.
1007
- def initialize_schema_migrations_table
1008
- ActiveRecord::SchemaMigration.create_table
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)
1009
1184
  end
1010
1185
 
1011
- def initialize_internal_metadata_table
1012
- ActiveRecord::InternalMetadata.create_table
1186
+ def dump_schema_information # :nodoc:
1187
+ versions = schema_migration.all_versions
1188
+ insert_versions_sql(versions) if versions.any?
1013
1189
  end
1014
1190
 
1015
1191
  def internal_string_options_for_primary_key # :nodoc:
1016
1192
  { primary_key: true }
1017
1193
  end
1018
1194
 
1019
- def assume_migrated_upto_version(version, migrations_paths)
1020
- migrations_paths = Array(migrations_paths)
1195
+ def assume_migrated_upto_version(version)
1021
1196
  version = version.to_i
1022
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
1197
+ sm_table = quote_table_name(schema_migration.table_name)
1023
1198
 
1024
- migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
1025
- versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
1026
- ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
1027
- end
1199
+ migrated = migration_context.get_all_versions
1200
+ versions = migration_context.migrations.map(&:version)
1028
1201
 
1029
1202
  unless migrated.include?(version)
1030
1203
  execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
1031
1204
  end
1032
1205
 
1033
- inserting = (versions - migrated).select {|v| v < version}
1206
+ inserting = (versions - migrated).select { |v| v < version }
1034
1207
  if inserting.any?
1035
- if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
1208
+ if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1036
1209
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1037
1210
  end
1038
- if supports_multi_insert?
1039
- execute insert_versions_sql(inserting)
1040
- else
1041
- inserting.each do |v|
1042
- execute insert_versions_sql(v)
1043
- end
1044
- end
1211
+ execute insert_versions_sql(inserting)
1045
1212
  end
1046
1213
  end
1047
1214
 
1048
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
1215
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
1049
1216
  type = type.to_sym if type
1050
1217
  if native = native_database_types[type]
1051
1218
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
@@ -1063,11 +1230,11 @@ module ActiveRecord
1063
1230
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
1064
1231
  end
1065
1232
 
1066
- elsif [:datetime, :time].include?(type) && precision ||= native[:precision]
1233
+ elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
1067
1234
  if (0..6) === precision
1068
1235
  column_type_sql << "(#{precision})"
1069
1236
  else
1070
- 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"
1071
1238
  end
1072
1239
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1073
1240
  column_type_sql << "(#{limit})"
@@ -1080,7 +1247,7 @@ module ActiveRecord
1080
1247
  end
1081
1248
 
1082
1249
  # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
1083
- # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
1250
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
1084
1251
  # require the order columns appear in the SELECT.
1085
1252
  #
1086
1253
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
@@ -1094,18 +1261,22 @@ module ActiveRecord
1094
1261
  #
1095
1262
  # add_timestamps(:suppliers, null: true)
1096
1263
  #
1097
- def add_timestamps(table_name, options = {})
1264
+ def add_timestamps(table_name, **options)
1098
1265
  options[:null] = false if options[:null].nil?
1099
1266
 
1100
- add_column table_name, :created_at, :datetime, options
1101
- 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
1102
1273
  end
1103
1274
 
1104
1275
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1105
1276
  #
1106
1277
  # remove_timestamps(:suppliers)
1107
1278
  #
1108
- def remove_timestamps(table_name, options = {})
1279
+ def remove_timestamps(table_name, **options)
1109
1280
  remove_column table_name, :updated_at
1110
1281
  remove_column table_name, :created_at
1111
1282
  end
@@ -1114,38 +1285,43 @@ module ActiveRecord
1114
1285
  Table.new(table_name, base)
1115
1286
  end
1116
1287
 
1117
- def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1118
- 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)
1119
1290
 
1120
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
1291
+ column_names = index_column_names(column_name)
1121
1292
 
1122
- index_type = options[:type].to_s if options.key?(:type)
1123
- index_type ||= options[:unique] ? "UNIQUE" : ""
1124
- index_name = options[:name].to_s if options.key?(:name)
1293
+ index_name = name&.to_s
1125
1294
  index_name ||= index_name(table_name, column_names)
1126
- max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
1127
1295
 
1128
- if options.key?(:algorithm)
1129
- algorithm = index_algorithms.fetch(options[:algorithm]) {
1130
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
1131
- }
1132
- end
1296
+ validate_index_length!(table_name, index_name, internal)
1133
1297
 
1134
- using = "USING #{options[:using]}" if options[:using].present?
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
+ )
1135
1310
 
1136
- if supports_partial_index?
1137
- index_options = options[:where] ? " WHERE #{options[:where]}" : ""
1138
- end
1311
+ [index, index_algorithm(options[:algorithm]), if_not_exists]
1312
+ end
1139
1313
 
1140
- if index_name.length > max_index_length
1141
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
1142
- end
1143
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false)
1144
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
1145
- end
1146
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
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
1147
1319
 
1148
- [index_name, index_type, index_columns, index_options, algorithm, using, comment]
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
1323
+ end
1324
+ add_options_for_index_columns(quoted_columns, **options).values.join(", ")
1149
1325
  end
1150
1326
 
1151
1327
  def options_include_default?(options)
@@ -1153,63 +1329,73 @@ module ActiveRecord
1153
1329
  end
1154
1330
 
1155
1331
  # Changes the comment for a table or removes it if +nil+.
1156
- 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)
1157
1338
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1158
1339
  end
1159
1340
 
1160
1341
  # Changes the comment for a column or removes it if +nil+.
1161
- def change_column_comment(table_name, column_name, comment) #:nodoc:
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)
1162
1348
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1163
1349
  end
1164
1350
 
1165
- protected
1351
+ def create_schema_dumper(options) # :nodoc:
1352
+ SchemaDumper.create(self, options)
1353
+ end
1354
+
1355
+ private
1356
+ def column_options_keys
1357
+ [:limit, :precision, :scale, :default, :null, :collation, :comment]
1358
+ end
1166
1359
 
1167
1360
  def add_index_sort_order(quoted_columns, **options)
1168
- if order = options[:order]
1169
- case order
1170
- when Hash
1171
- order = order.symbolize_keys
1172
- quoted_columns.each { |name, column| column << " #{order[name].upcase}" if order[name].present? }
1173
- when String
1174
- quoted_columns.each { |name, column| column << " #{order.upcase}" if order.present? }
1175
- end
1361
+ orders = options_for_index_columns(options[:order])
1362
+ quoted_columns.each do |name, column|
1363
+ column << " #{orders[name].upcase}" if orders[name].present?
1176
1364
  end
1365
+ end
1177
1366
 
1178
- quoted_columns
1367
+ def options_for_index_columns(options)
1368
+ if options.is_a?(Hash)
1369
+ options.symbolize_keys
1370
+ else
1371
+ Hash.new { |hash, column| hash[column] = options }
1372
+ end
1179
1373
  end
1180
1374
 
1181
- # Overridden by the MySQL adapter for supporting index lengths
1375
+ # Overridden by the MySQL adapter for supporting index lengths and by
1376
+ # the PostgreSQL adapter for supporting operator classes.
1182
1377
  def add_options_for_index_columns(quoted_columns, **options)
1183
1378
  if supports_index_sort_order?
1184
- quoted_columns = add_index_sort_order(quoted_columns, options)
1379
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
1185
1380
  end
1186
1381
 
1187
1382
  quoted_columns
1188
1383
  end
1189
1384
 
1190
- def quoted_columns_for_index(column_names, **options)
1191
- return [column_names] if column_names.is_a?(String)
1192
-
1193
- quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1194
- add_options_for_index_columns(quoted_columns, options).values
1195
- end
1196
-
1197
- def index_name_for_remove(table_name, options = {})
1198
- return options[:name] if can_remove_index_by_name?(options)
1199
-
1200
- # if the adapter doesn't support the indexes call the best we can do
1201
- # is return the default index name for the options provided
1202
- return index_name(table_name, options) unless respond_to?(:indexes)
1385
+ def index_name_for_remove(table_name, column_name, options)
1386
+ return options[:name] if can_remove_index_by_name?(column_name, options)
1203
1387
 
1204
1388
  checks = []
1205
1389
 
1206
- if options.is_a?(Hash)
1207
- checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1208
- 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 = []
1209
1393
  else
1210
- column_names = index_column_names(options)
1394
+ column_names = index_column_names(column_name || options[:column])
1211
1395
  end
1212
1396
 
1397
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1398
+
1213
1399
  if column_names.present?
1214
1400
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1215
1401
  end
@@ -1250,56 +1436,202 @@ module ActiveRecord
1250
1436
  end
1251
1437
  end
1252
1438
 
1253
- private
1254
- def create_table_definition(*args)
1255
- TableDefinition.new(*args)
1256
- end
1439
+ def schema_creation
1440
+ SchemaCreation.new(self)
1441
+ end
1257
1442
 
1258
- def create_alter_table(name)
1259
- AlterTable.new create_table_definition(name)
1260
- end
1443
+ def create_table_definition(name, **options)
1444
+ TableDefinition.new(self, name, **options)
1445
+ end
1261
1446
 
1262
- def index_column_names(column_names)
1263
- if column_names.is_a?(String) && /\W/ === column_names
1264
- column_names
1265
- else
1266
- Array(column_names)
1447
+ def create_alter_table(name)
1448
+ AlterTable.new create_table_definition(name)
1267
1449
  end
1268
- end
1269
1450
 
1270
- def index_name_options(column_names) # :nodoc:
1271
- if column_names.is_a?(String) && /\W/ === column_names
1272
- column_names = column_names.scan(/\w+/).join('_')
1451
+ def extract_table_options!(options)
1452
+ options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1273
1453
  end
1274
1454
 
1275
- { column: column_names }
1276
- end
1455
+ def fetch_type_metadata(sql_type)
1456
+ cast_type = lookup_cast_type(sql_type)
1457
+ SqlTypeMetadata.new(
1458
+ sql_type: sql_type,
1459
+ type: cast_type.type,
1460
+ limit: cast_type.limit,
1461
+ precision: cast_type.precision,
1462
+ scale: cast_type.scale,
1463
+ )
1464
+ end
1465
+
1466
+ def index_column_names(column_names)
1467
+ if column_names.is_a?(String) && /\W/.match?(column_names)
1468
+ column_names
1469
+ else
1470
+ Array(column_names)
1471
+ end
1472
+ end
1473
+
1474
+ def index_name_options(column_names)
1475
+ if column_names.is_a?(String) && /\W/.match?(column_names)
1476
+ column_names = column_names.scan(/\w+/).join("_")
1477
+ end
1277
1478
 
1278
- def foreign_key_name(table_name, options) # :nodoc:
1279
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1280
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1281
- options.fetch(:name) do
1282
- "fk_rails_#{hashed_identifier}"
1479
+ { column: column_names }
1283
1480
  end
1284
- end
1285
1481
 
1286
- def validate_index_length!(table_name, new_name) # :nodoc:
1287
- if new_name.length > allowed_index_name_length
1288
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
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
1289
1486
  end
1290
- end
1291
1487
 
1292
- def extract_new_default_value(default_or_changes)
1293
- if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1294
- default_or_changes[:to]
1295
- else
1296
- default_or_changes
1488
+ def foreign_key_name(table_name, options)
1489
+ options.fetch(:name) do
1490
+ identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1491
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1492
+
1493
+ "fk_rails_#{hashed_identifier}"
1494
+ end
1297
1495
  end
1298
- end
1299
1496
 
1300
- def can_remove_index_by_name?(options)
1301
- options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1302
- end
1497
+ def foreign_key_for(from_table, **options)
1498
+ return unless supports_foreign_keys?
1499
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1500
+ end
1501
+
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}")
1505
+ end
1506
+
1507
+ def extract_foreign_key_action(specifier)
1508
+ case specifier
1509
+ when "CASCADE"; :cascade
1510
+ when "SET NULL"; :nullify
1511
+ when "RESTRICT"; :restrict
1512
+ end
1513
+ end
1514
+
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)
1520
+
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"
1539
+ end
1540
+ end
1541
+
1542
+ def extract_new_default_value(default_or_changes)
1543
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1544
+ default_or_changes[:to]
1545
+ else
1546
+ default_or_changes
1547
+ end
1548
+ end
1549
+ alias :extract_new_comment_value :extract_new_default_value
1550
+
1551
+ def can_remove_index_by_name?(column_name, options)
1552
+ column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1553
+ end
1554
+
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)
1581
+ td = create_table_definition(table_name)
1582
+ cd = td.new_column_definition(column_name, type, **options)
1583
+ schema_creation.accept(AddColumnDefinition.new(cd))
1584
+ end
1585
+
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)
1591
+ "DROP COLUMN #{quote_column_name(column_name)}"
1592
+ end
1593
+
1594
+ def remove_columns_for_alter(table_name, *column_names, **options)
1595
+ column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1596
+ end
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
+
1615
+ def insert_versions_sql(versions)
1616
+ sm_table = quote_table_name(schema_migration.table_name)
1617
+
1618
+ if versions.is_a?(Array)
1619
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1620
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1621
+ sql << ";\n\n"
1622
+ sql
1623
+ else
1624
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1625
+ end
1626
+ end
1627
+
1628
+ def data_source_sql(name = nil, type: nil)
1629
+ raise NotImplementedError
1630
+ end
1631
+
1632
+ def quoted_scope(name = nil, type: nil)
1633
+ raise NotImplementedError
1634
+ end
1303
1635
  end
1304
1636
  end
1305
1637
  end