activerecord 4.2.11.2 → 6.0.2.2

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 (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1582
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  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 +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +5 -3
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,50 +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, types)
10
- spec = prepare_column_options(column, types)
11
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")}
12
- spec
5
+ class SchemaDumper < SchemaDumper # :nodoc:
6
+ def self.create(connection, options)
7
+ new(connection, options)
13
8
  end
14
9
 
15
- # This can be overridden on a Adapter level basis to support other
16
- # extended datatypes (Example: Adding an array option in the
17
- # PostgreSQLAdapter)
18
- def prepare_column_options(column, types)
19
- spec = {}
20
- spec[:name] = column.name.inspect
21
- spec[:type] = column.type.to_s
22
- spec[:null] = 'false' unless column.null
10
+ private
11
+ def column_spec(column)
12
+ [schema_type_with_virtual(column), prepare_column_options(column)]
13
+ end
23
14
 
24
- limit = column.limit || types[column.type][:limit]
25
- spec[:limit] = limit.inspect if limit
26
- spec[:precision] = column.precision.inspect if column.precision
27
- spec[:scale] = column.scale.inspect if column.scale
15
+ def column_spec_for_primary_key(column)
16
+ return {} if default_primary_key?(column)
17
+ spec = { id: schema_type(column).inspect }
18
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
19
+ spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
+ spec
21
+ end
28
22
 
29
- default = schema_default(column) if column.has_default?
30
- spec[:default] = default unless default.nil?
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
34
+ end
31
35
 
32
- spec
33
- end
36
+ def default_primary_key?(column)
37
+ schema_type(column) == :bigint
38
+ end
34
39
 
35
- # Lists the valid migration options
36
- def migration_keys
37
- [:name, :limit, :precision, :scale, :default, :null]
38
- end
40
+ def explicit_primary_key_default?(column)
41
+ false
42
+ end
39
43
 
40
- private
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
41
51
 
42
- def schema_default(column)
43
- default = column.type_cast_from_database(column.default)
44
- unless default.nil?
45
- column.type_cast_for_schema(default)
52
+ def schema_type(column)
53
+ if column.bigint?
54
+ :bigint
55
+ else
56
+ column.type
57
+ end
58
+ end
59
+
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
64
+
65
+ def schema_precision(column)
66
+ column.precision.inspect if column.precision
67
+ end
68
+
69
+ def schema_scale(column)
70
+ column.scale.inspect if column.scale
71
+ end
72
+
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
82
+ end
83
+
84
+ def schema_expression(column)
85
+ "-> { #{column.default_function.inspect} }" if column.default_function
86
+ end
87
+
88
+ def schema_collation(column)
89
+ column.collation.inspect if column.collation
46
90
  end
47
- end
48
91
  end
49
92
  end
50
93
  end
@@ -1,6 +1,9 @@
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_record/migration/join_table"
4
+ require "active_support/core_ext/string/access"
5
+ require "active_support/deprecation"
6
+ require "digest/sha2"
4
7
 
5
8
  module ActiveRecord
6
9
  module ConnectionAdapters # :nodoc:
@@ -14,15 +17,26 @@ module ActiveRecord
14
17
  {}
15
18
  end
16
19
 
20
+ def table_options(table_name)
21
+ nil
22
+ end
23
+
24
+ # Returns the table comment that's stored in database metadata.
25
+ def table_comment(table_name)
26
+ nil
27
+ end
28
+
17
29
  # Truncates a table alias according to the limits of the current adapter.
18
30
  def table_alias_for(table_name)
19
- table_name[0...table_alias_length].tr('.', '_')
31
+ table_name[0...table_alias_length].tr(".", "_")
20
32
  end
21
33
 
22
34
  # Returns the relation names useable to back Active Record models.
23
- # For most adapters this means all tables and views.
35
+ # For most adapters this means all #tables and #views.
24
36
  def data_sources
25
- tables
37
+ query_values(data_source_sql, "SCHEMA")
38
+ rescue NotImplementedError
39
+ tables | views
26
40
  end
27
41
 
28
42
  # Checks to see if the data source +name+ exists on the database.
@@ -30,19 +44,45 @@ module ActiveRecord
30
44
  # data_source_exists?(:ebooks)
31
45
  #
32
46
  def data_source_exists?(name)
47
+ query_values(data_source_sql(name), "SCHEMA").any? if name.present?
48
+ rescue NotImplementedError
33
49
  data_sources.include?(name.to_s)
34
50
  end
35
51
 
52
+ # Returns an array of table names defined in the database.
53
+ def tables
54
+ query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
55
+ end
56
+
36
57
  # Checks to see if the table +table_name+ exists on the database.
37
58
  #
38
59
  # table_exists?(:developers)
39
60
  #
40
61
  def table_exists?(table_name)
62
+ query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
63
+ rescue NotImplementedError
41
64
  tables.include?(table_name.to_s)
42
65
  end
43
66
 
67
+ # Returns an array of view names defined in the database.
68
+ def views
69
+ query_values(data_source_sql(type: "VIEW"), "SCHEMA")
70
+ end
71
+
72
+ # Checks to see if the view +view_name+ exists on the database.
73
+ #
74
+ # view_exists?(:ebooks)
75
+ #
76
+ def view_exists?(view_name)
77
+ query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
78
+ rescue NotImplementedError
79
+ views.include?(view_name.to_s)
80
+ end
81
+
44
82
  # Returns an array of indexes for the given table.
45
- # def indexes(table_name, name = nil) end
83
+ def indexes(table_name)
84
+ raise NotImplementedError, "#indexes is not implemented"
85
+ end
46
86
 
47
87
  # Checks to see if an index exists on a table for a given index definition.
48
88
  #
@@ -60,18 +100,21 @@ module ActiveRecord
60
100
  #
61
101
  def index_exists?(table_name, column_name, options = {})
62
102
  column_names = Array(column_name).map(&:to_s)
63
- index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
64
103
  checks = []
65
- checks << lambda { |i| i.name == index_name }
66
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
67
105
  checks << lambda { |i| i.unique } if options[:unique]
106
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
68
107
 
69
108
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
70
109
  end
71
110
 
72
- # Returns an array of Column objects for the table specified by +table_name+.
73
- # See the concrete implementation for details on the expected parameter values.
74
- def columns(table_name) end
111
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
112
+ def columns(table_name)
113
+ table_name = table_name.to_s
114
+ column_definitions(table_name).map do |field|
115
+ new_column_from_field(table_name, field)
116
+ end
117
+ end
75
118
 
76
119
  # Checks to see if a column exists in a given table.
77
120
  #
@@ -87,21 +130,29 @@ module ActiveRecord
87
130
  # column_exists?(:suppliers, :name, :string, null: false)
88
131
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
89
132
  #
90
- def column_exists?(table_name, column_name, type = nil, options = {})
133
+ def column_exists?(table_name, column_name, type = nil, **options)
91
134
  column_name = column_name.to_s
92
- columns(table_name).any?{ |c| c.name == column_name &&
93
- (!type || c.type == type) &&
94
- (!options.key?(:limit) || c.limit == options[:limit]) &&
95
- (!options.key?(:precision) || c.precision == options[:precision]) &&
96
- (!options.key?(:scale) || c.scale == options[:scale]) &&
97
- (!options.key?(:default) || c.default == options[:default]) &&
98
- (!options.key?(:null) || c.null == options[:null]) }
135
+ checks = []
136
+ checks << lambda { |c| c.name == column_name }
137
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
138
+ column_options_keys.each do |attr|
139
+ checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
140
+ end
141
+
142
+ columns(table_name).any? { |c| checks.all? { |check| check[c] } }
143
+ end
144
+
145
+ # Returns just a table's primary key
146
+ def primary_key(table_name)
147
+ pk = primary_keys(table_name)
148
+ pk = pk.first unless pk.size > 1
149
+ pk
99
150
  end
100
151
 
101
152
  # Creates a new table with the name +table_name+. +table_name+ may either
102
153
  # be a String or a Symbol.
103
154
  #
104
- # There are two ways to work with +create_table+. You can use the block
155
+ # There are two ways to work with #create_table. You can use the block
105
156
  # form or the regular form, like this:
106
157
  #
107
158
  # === Block form
@@ -133,13 +184,18 @@ module ActiveRecord
133
184
  # The +options+ hash can include the following keys:
134
185
  # [<tt>:id</tt>]
135
186
  # Whether to automatically add a primary key column. Defaults to true.
136
- # Join tables for +has_and_belongs_to_many+ should set it to false.
187
+ # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
188
+ #
189
+ # A Symbol can be used to specify the type of the generated primary key column.
137
190
  # [<tt>:primary_key</tt>]
138
191
  # The name of the primary key, if one is to be added automatically.
139
- # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
192
+ # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
193
+ #
194
+ # If an array is passed, a composite primary key will be created.
140
195
  #
141
196
  # Note that Active Record models will automatically detect their
142
- # primary key. This can be avoided by using +self.primary_key=+ on the model
197
+ # primary key. This can be avoided by using
198
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
143
199
  # to define the key explicitly.
144
200
  #
145
201
  # [<tt>:options</tt>]
@@ -150,19 +206,22 @@ module ActiveRecord
150
206
  # Set to true to drop the table before creating it.
151
207
  # Set to +:cascade+ to drop dependent objects as well.
152
208
  # Defaults to false.
209
+ # [<tt>:if_not_exists</tt>]
210
+ # Set to true to avoid raising an error when the table already exists.
211
+ # Defaults to false.
153
212
  # [<tt>:as</tt>]
154
213
  # SQL to use to generate the table. When this option is used, the block is
155
214
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
156
215
  #
157
216
  # ====== Add a backend specific option to the generated SQL (MySQL)
158
217
  #
159
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
160
219
  #
161
220
  # generates:
162
221
  #
163
222
  # CREATE TABLE suppliers (
164
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
165
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
223
+ # id bigint auto_increment PRIMARY KEY
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
166
225
  #
167
226
  # ====== Rename the primary key column
168
227
  #
@@ -173,22 +232,52 @@ module ActiveRecord
173
232
  # generates:
174
233
  #
175
234
  # CREATE TABLE objects (
176
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
235
+ # guid bigint auto_increment PRIMARY KEY,
177
236
  # name varchar(80)
178
237
  # )
179
238
  #
239
+ # ====== Change the primary key column type
240
+ #
241
+ # create_table(:tags, id: :string) do |t|
242
+ # t.column :label, :string
243
+ # end
244
+ #
245
+ # generates:
246
+ #
247
+ # CREATE TABLE tags (
248
+ # id varchar PRIMARY KEY,
249
+ # label varchar
250
+ # )
251
+ #
252
+ # ====== Create a composite primary key
253
+ #
254
+ # create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
255
+ # t.belongs_to :product
256
+ # t.belongs_to :client
257
+ # end
258
+ #
259
+ # generates:
260
+ #
261
+ # CREATE TABLE order (
262
+ # product_id bigint NOT NULL,
263
+ # client_id bigint NOT NULL
264
+ # );
265
+ #
266
+ # ALTER TABLE ONLY "orders"
267
+ # ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
268
+ #
180
269
  # ====== Do not add a primary key column
181
270
  #
182
271
  # create_table(:categories_suppliers, id: false) do |t|
183
- # t.column :category_id, :integer
184
- # t.column :supplier_id, :integer
272
+ # t.column :category_id, :bigint
273
+ # t.column :supplier_id, :bigint
185
274
  # end
186
275
  #
187
276
  # generates:
188
277
  #
189
278
  # CREATE TABLE categories_suppliers (
190
- # category_id int,
191
- # supplier_id int
279
+ # category_id bigint,
280
+ # supplier_id bigint
192
281
  # )
193
282
  #
194
283
  # ====== Create a temporary table based on a query
@@ -202,33 +291,43 @@ module ActiveRecord
202
291
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
203
292
  #
204
293
  # See also TableDefinition#column for details on how to create columns.
205
- def create_table(table_name, options = {})
206
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
294
+ def create_table(table_name, **options)
295
+ td = create_table_definition(table_name, options)
207
296
 
208
297
  if options[:id] != false && !options[:as]
209
298
  pk = options.fetch(:primary_key) do
210
299
  Base.get_primary_key table_name.to_s.singularize
211
300
  end
212
301
 
213
- td.primary_key pk, options.fetch(:id, :primary_key), options
302
+ if pk.is_a?(Array)
303
+ td.primary_keys pk
304
+ else
305
+ td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
306
+ end
214
307
  end
215
308
 
216
309
  yield td if block_given?
217
310
 
218
- if options[:force] && table_exists?(table_name)
219
- drop_table(table_name, options)
311
+ if options[:force]
312
+ drop_table(table_name, options.merge(if_exists: true))
220
313
  end
221
314
 
222
315
  result = execute schema_creation.accept td
223
316
 
224
317
  unless supports_indexes_in_create?
225
- td.indexes.each_pair do |column_name, index_options|
318
+ td.indexes.each do |column_name, index_options|
226
319
  add_index(table_name, column_name, index_options)
227
320
  end
228
321
  end
229
322
 
230
- td.foreign_keys.each do |other_table_name, foreign_key_options|
231
- add_foreign_key(table_name, other_table_name, foreign_key_options)
323
+ if supports_comments? && !supports_comments_in_create?
324
+ if table_comment = options[:comment].presence
325
+ change_table_comment(table_name, table_comment)
326
+ end
327
+
328
+ td.columns.each do |column|
329
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
330
+ end
232
331
  end
233
332
 
234
333
  result
@@ -240,9 +339,9 @@ module ActiveRecord
240
339
  # # Creates a table called 'assemblies_parts' with no id.
241
340
  # create_join_table(:assemblies, :parts)
242
341
  #
243
- # You can pass a +options+ hash can include the following keys:
342
+ # You can pass an +options+ hash which can include the following keys:
244
343
  # [<tt>:table_name</tt>]
245
- # Sets the table name overriding the default
344
+ # Sets the table name, overriding the default.
246
345
  # [<tt>:column_options</tt>]
247
346
  # Any extra options you want appended to the columns definition.
248
347
  # [<tt>:options</tt>]
@@ -253,7 +352,7 @@ module ActiveRecord
253
352
  # Set to true to drop the table before creating it.
254
353
  # Defaults to false.
255
354
  #
256
- # Note that +create_join_table+ does not create any indices by default; you can use
355
+ # Note that #create_join_table does not create any indices by default; you can use
257
356
  # its block form to do so yourself:
258
357
  #
259
358
  # create_join_table :products, :categories do |t|
@@ -268,31 +367,30 @@ module ActiveRecord
268
367
  # generates:
269
368
  #
270
369
  # CREATE TABLE assemblies_parts (
271
- # assembly_id int NOT NULL,
272
- # part_id int NOT NULL,
370
+ # assembly_id bigint NOT NULL,
371
+ # part_id bigint NOT NULL,
273
372
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
274
373
  #
275
- def create_join_table(table_1, table_2, options = {})
374
+ def create_join_table(table_1, table_2, column_options: {}, **options)
276
375
  join_table_name = find_join_table_name(table_1, table_2, options)
277
376
 
278
- column_options = options.delete(:column_options) || {}
279
- column_options.reverse_merge!(null: false)
377
+ column_options.reverse_merge!(null: false, index: false)
280
378
 
281
- t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
379
+ t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
282
380
 
283
381
  create_table(join_table_name, options.merge!(id: false)) do |td|
284
- td.integer t1_column, column_options
285
- td.integer t2_column, column_options
382
+ td.references t1_ref, column_options
383
+ td.references t2_ref, column_options
286
384
  yield td if block_given?
287
385
  end
288
386
  end
289
387
 
290
388
  # Drops the join table specified by the given arguments.
291
- # See +create_join_table+ for details.
389
+ # See #create_join_table for details.
292
390
  #
293
391
  # Although this command ignores the block if one is given, it can be helpful
294
392
  # to provide one in a migration's +change+ method so it can be reverted.
295
- # In that case, the block will be used by create_join_table.
393
+ # In that case, the block will be used by #create_join_table.
296
394
  def drop_join_table(table_1, table_2, options = {})
297
395
  join_table_name = find_join_table_name(table_1, table_2, options)
298
396
  drop_table(join_table_name)
@@ -310,10 +408,12 @@ module ActiveRecord
310
408
  # [<tt>:bulk</tt>]
311
409
  # Set this to true to make this a bulk alter query, such as
312
410
  #
313
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
411
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
314
412
  #
315
413
  # Defaults to false.
316
414
  #
415
+ # Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
416
+ #
317
417
  # ====== Add a column
318
418
  #
319
419
  # change_table(:suppliers) do |t|
@@ -338,7 +438,7 @@ module ActiveRecord
338
438
  # t.references :company
339
439
  # end
340
440
  #
341
- # Creates a <tt>company_id(integer)</tt> column.
441
+ # Creates a <tt>company_id(bigint)</tt> column.
342
442
  #
343
443
  # ====== Add a polymorphic foreign key column
344
444
  #
@@ -346,7 +446,7 @@ module ActiveRecord
346
446
  # t.belongs_to :company, polymorphic: true
347
447
  # end
348
448
  #
349
- # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
449
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
350
450
  #
351
451
  # ====== Remove a column
352
452
  #
@@ -367,7 +467,7 @@ module ActiveRecord
367
467
  # t.remove_index :company_id
368
468
  # end
369
469
  #
370
- # See also Table for details on all of the various column transformation.
470
+ # See also Table for details on all of the various column transformations.
371
471
  def change_table(table_name, options = {})
372
472
  if supports_bulk_alter? && options[:bulk]
373
473
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
@@ -391,17 +491,101 @@ module ActiveRecord
391
491
  # [<tt>:force</tt>]
392
492
  # Set to +:cascade+ to drop dependent objects as well.
393
493
  # Defaults to false.
494
+ # [<tt>:if_exists</tt>]
495
+ # Set to +true+ to only drop the table if it exists.
496
+ # Defaults to false.
394
497
  #
395
498
  # Although this command ignores most +options+ and the block if one is given,
396
499
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
397
- # In that case, +options+ and the block will be used by create_table.
500
+ # In that case, +options+ and the block will be used by #create_table.
398
501
  def drop_table(table_name, options = {})
399
- execute "DROP TABLE #{quote_table_name(table_name)}"
502
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
400
503
  end
401
504
 
402
- # Adds a new column to the named table.
403
- # See TableDefinition#column for details of the options you can use.
404
- def add_column(table_name, column_name, type, options = {})
505
+ # Add a new +type+ column named +column_name+ to +table_name+.
506
+ #
507
+ # The +type+ parameter is normally one of the migrations native types,
508
+ # which is one of the following:
509
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
510
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
511
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
512
+ # <tt>:binary</tt>, <tt>:boolean</tt>.
513
+ #
514
+ # You may use a type not in this list as long as it is supported by your
515
+ # database (for example, "polygon" in MySQL), but this will not be database
516
+ # agnostic and should usually be avoided.
517
+ #
518
+ # Available options are (none of these exists by default):
519
+ # * <tt>:limit</tt> -
520
+ # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
521
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
522
+ # This option is ignored by some backends.
523
+ # * <tt>:default</tt> -
524
+ # The column's default value. Use +nil+ for +NULL+.
525
+ # * <tt>:null</tt> -
526
+ # Allows or disallows +NULL+ values in the column.
527
+ # * <tt>:precision</tt> -
528
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
529
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
530
+ # * <tt>:scale</tt> -
531
+ # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
532
+ # * <tt>:collation</tt> -
533
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
534
+ # column will have the same collation as the table.
535
+ # * <tt>:comment</tt> -
536
+ # Specifies the comment for the column. This option is ignored by some backends.
537
+ #
538
+ # Note: The precision is the total number of significant digits,
539
+ # and the scale is the number of digits that can be stored following
540
+ # the decimal point. For example, the number 123.45 has a precision of 5
541
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
542
+ # range from -999.99 to 999.99.
543
+ #
544
+ # Please be aware of different RDBMS implementations behavior with
545
+ # <tt>:decimal</tt> columns:
546
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
547
+ # <tt>:precision</tt>, and makes no comments about the requirements of
548
+ # <tt>:precision</tt>.
549
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
550
+ # Default is (10,0).
551
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
552
+ # <tt>:scale</tt> [0..infinity]. No default.
553
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
554
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
555
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
556
+ # Default is (38,0).
557
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
558
+ # Default unknown.
559
+ # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
560
+ # Default (38,0).
561
+ #
562
+ # == Examples
563
+ #
564
+ # add_column(:users, :picture, :binary, limit: 2.megabytes)
565
+ # # ALTER TABLE "users" ADD "picture" blob(2097152)
566
+ #
567
+ # add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
568
+ # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
569
+ #
570
+ # add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
571
+ # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
572
+ #
573
+ # add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
574
+ # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
575
+ #
576
+ # # While :scale defaults to zero on most databases, it
577
+ # # probably wouldn't hurt to include it.
578
+ # add_column(:measurements, :huge_integer, :decimal, precision: 30)
579
+ # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
580
+ #
581
+ # # Defines a column that stores an array of a type.
582
+ # add_column(:users, :skills, :text, array: true)
583
+ # # ALTER TABLE "users" ADD "skills" text[]
584
+ #
585
+ # # Defines a column with a database-specific type.
586
+ # add_column(:shapes, :triangle, 'polygon')
587
+ # # ALTER TABLE "shapes" ADD "triangle" polygon
588
+ def add_column(table_name, column_name, type, **options)
405
589
  at = create_alter_table table_name
406
590
  at.add_column(column_name, type, options)
407
591
  execute schema_creation.accept at
@@ -424,9 +608,10 @@ module ActiveRecord
424
608
  #
425
609
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
426
610
  # to provide these in a migration's +change+ method so it can be reverted.
427
- # In that case, +type+ and +options+ will be used by add_column.
611
+ # In that case, +type+ and +options+ will be used by #add_column.
612
+ # Indexes on the column are automatically removed.
428
613
  def remove_column(table_name, column_name, type = nil, options = {})
429
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
614
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
430
615
  end
431
616
 
432
617
  # Changes the column's definition according to the new options.
@@ -448,11 +633,16 @@ module ActiveRecord
448
633
  #
449
634
  # change_column_default(:users, :email, nil)
450
635
  #
451
- def change_column_default(table_name, column_name, default)
636
+ # Passing a hash containing +:from+ and +:to+ will make this change
637
+ # reversible in migration:
638
+ #
639
+ # change_column_default(:posts, :state, from: nil, to: "draft")
640
+ #
641
+ def change_column_default(table_name, column_name, default_or_changes)
452
642
  raise NotImplementedError, "change_column_default is not implemented"
453
643
  end
454
644
 
455
- # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
645
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
456
646
  # indicates whether the value can be +NULL+. For example
457
647
  #
458
648
  # change_column_null(:users, :nickname, false)
@@ -464,7 +654,7 @@ module ActiveRecord
464
654
  # allows them to be +NULL+ (drops the constraint).
465
655
  #
466
656
  # The method accepts an optional fourth argument to replace existing
467
- # +NULL+s with some other value. Use that one when enabling the
657
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
468
658
  # constraint if needed, since otherwise those rows would not be valid.
469
659
  #
470
660
  # Please note the fourth argument does not set a column's default.
@@ -518,6 +708,8 @@ module ActiveRecord
518
708
  #
519
709
  # CREATE INDEX by_name ON accounts(name(10))
520
710
  #
711
+ # ====== Creating an index with specific key lengths for multiple keys
712
+ #
521
713
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
522
714
  #
523
715
  # generates:
@@ -534,7 +726,7 @@ module ActiveRecord
534
726
  #
535
727
  # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
536
728
  #
537
- # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
729
+ # Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
538
730
  #
539
731
  # ====== Creating a partial index
540
732
  #
@@ -557,6 +749,19 @@ module ActiveRecord
557
749
  #
558
750
  # Note: only supported by PostgreSQL and MySQL
559
751
  #
752
+ # ====== Creating an index with a specific operator class
753
+ #
754
+ # add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
755
+ # # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
756
+ #
757
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
758
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
759
+ #
760
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
761
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
762
+ #
763
+ # Note: only supported by PostgreSQL
764
+ #
560
765
  # ====== Creating an index with a specific type
561
766
  #
562
767
  # add_index(:developers, :name, type: :fulltext)
@@ -565,7 +770,18 @@ module ActiveRecord
565
770
  #
566
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
567
772
  #
568
- # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
773
+ # Note: only supported by MySQL.
774
+ #
775
+ # ====== Creating an index with a specific algorithm
776
+ #
777
+ # add_index(:developers, :name, algorithm: :concurrently)
778
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
779
+ #
780
+ # Note: only supported by PostgreSQL.
781
+ #
782
+ # Concurrently adding an index is not supported in a transaction.
783
+ #
784
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
569
785
  def add_index(table_name, column_name, options = {})
570
786
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
571
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -573,15 +789,15 @@ module ActiveRecord
573
789
 
574
790
  # Removes the given index from the table.
575
791
  #
576
- # Removes the +index_accounts_on_column+ in the +accounts+ table.
792
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
577
793
  #
578
- # remove_index :accounts, :column
794
+ # remove_index :accounts, :branch_id
579
795
  #
580
- # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
796
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
581
797
  #
582
798
  # remove_index :accounts, column: :branch_id
583
799
  #
584
- # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
800
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
585
801
  #
586
802
  # remove_index :accounts, column: [:branch_id, :party_id]
587
803
  #
@@ -589,11 +805,17 @@ module ActiveRecord
589
805
  #
590
806
  # remove_index :accounts, name: :by_branch_party
591
807
  #
808
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
+ #
810
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
811
+ #
812
+ # Note: only supported by PostgreSQL.
813
+ #
814
+ # Concurrently removing an index is not supported in a transaction.
815
+ #
816
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
592
817
  def remove_index(table_name, options = {})
593
- remove_index!(table_name, index_name_for_remove(table_name, options))
594
- end
595
-
596
- def remove_index!(table_name, index_name) #:nodoc:
818
+ index_name = index_name_for_remove(table_name, options)
597
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
598
820
  end
599
821
 
@@ -606,7 +828,7 @@ module ActiveRecord
606
828
  def rename_index(table_name, old_name, new_name)
607
829
  validate_index_length!(table_name, new_name)
608
830
 
609
- # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
831
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
610
832
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
611
833
  return unless old_index_def
612
834
  add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
@@ -623,73 +845,73 @@ module ActiveRecord
623
845
  raise ArgumentError, "You must specify the index name"
624
846
  end
625
847
  else
626
- index_name(table_name, :column => options)
848
+ index_name(table_name, index_name_options(options))
627
849
  end
628
850
  end
629
851
 
630
852
  # Verifies the existence of an index with a given name.
631
- #
632
- # The default argument is returned if the underlying implementation does not define the indexes method,
633
- # as there's no way to determine the correct answer in that case.
634
- def index_name_exists?(table_name, index_name, default)
635
- return default unless respond_to?(:indexes)
853
+ def index_name_exists?(table_name, index_name)
636
854
  index_name = index_name.to_s
637
855
  indexes(table_name).detect { |i| i.name == index_name }
638
856
  end
639
857
 
640
- # Adds a reference. The reference column is an integer by default,
858
+ # Adds a reference. The reference column is a bigint by default,
641
859
  # the <tt>:type</tt> option can be used to specify a different type.
642
860
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
643
- # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
861
+ # #add_reference and #add_belongs_to are acceptable.
644
862
  #
645
863
  # The +options+ hash can include the following keys:
646
864
  # [<tt>:type</tt>]
647
- # The reference column type. Defaults to +:integer+.
865
+ # The reference column type. Defaults to +:bigint+.
648
866
  # [<tt>:index</tt>]
649
- # Add an appropriate index. Defaults to false.
867
+ # Add an appropriate index. Defaults to true.
868
+ # See #add_index for usage of this option.
650
869
  # [<tt>:foreign_key</tt>]
651
- # Add an appropriate foreign key. Defaults to false.
870
+ # Add an appropriate foreign key constraint. Defaults to false.
652
871
  # [<tt>:polymorphic</tt>]
653
- # Wether an additional +_type+ column should be added. Defaults to false.
872
+ # Whether an additional +_type+ column should be added. Defaults to false.
873
+ # [<tt>:null</tt>]
874
+ # Whether the column allows nulls. Defaults to true.
654
875
  #
655
- # ====== Create a user_id integer column
876
+ # ====== Create a user_id bigint column without an index
656
877
  #
657
- # add_reference(:products, :user)
878
+ # add_reference(:products, :user, index: false)
658
879
  #
659
880
  # ====== Create a user_id string column
660
881
  #
661
882
  # add_reference(:products, :user, type: :string)
662
883
  #
663
- # ====== Create supplier_id, supplier_type columns and appropriate index
884
+ # ====== Create supplier_id, supplier_type columns
664
885
  #
665
- # add_reference(:products, :supplier, polymorphic: true, index: true)
886
+ # add_reference(:products, :supplier, polymorphic: true)
666
887
  #
667
- def add_reference(table_name, ref_name, options = {})
668
- polymorphic = options.delete(:polymorphic)
669
- index_options = options.delete(:index)
670
- type = options.delete(:type) || :integer
671
- foreign_key_options = options.delete(:foreign_key)
672
-
673
- if polymorphic && foreign_key_options
674
- raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
675
- end
676
-
677
- add_column(table_name, "#{ref_name}_id", type, options)
678
- add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
679
- add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
680
- if foreign_key_options
681
- to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
682
- add_foreign_key(table_name, to_table, foreign_key_options.is_a?(Hash) ? foreign_key_options : {})
683
- end
888
+ # ====== Create a supplier_id column with a unique index
889
+ #
890
+ # add_reference(:products, :supplier, index: { unique: true })
891
+ #
892
+ # ====== Create a supplier_id column with a named index
893
+ #
894
+ # add_reference(:products, :supplier, index: { name: "my_supplier_index" })
895
+ #
896
+ # ====== Create a supplier_id column and appropriate foreign key
897
+ #
898
+ # add_reference(:products, :supplier, foreign_key: true)
899
+ #
900
+ # ====== Create a supplier_id column and a foreign key to the firms table
901
+ #
902
+ # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
903
+ #
904
+ def add_reference(table_name, ref_name, **options)
905
+ ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
684
906
  end
685
907
  alias :add_belongs_to :add_reference
686
908
 
687
909
  # Removes the reference(s). Also removes a +type+ column if one exists.
688
- # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
910
+ # #remove_reference and #remove_belongs_to are acceptable.
689
911
  #
690
912
  # ====== Remove the reference
691
913
  #
692
- # remove_reference(:products, :user, index: true)
914
+ # remove_reference(:products, :user, index: false)
693
915
  #
694
916
  # ====== Remove polymorphic reference
695
917
  #
@@ -697,21 +919,27 @@ module ActiveRecord
697
919
  #
698
920
  # ====== Remove the reference with a foreign key
699
921
  #
700
- # remove_reference(:products, :user, index: true, foreign_key: true)
922
+ # remove_reference(:products, :user, foreign_key: true)
701
923
  #
702
- def remove_reference(table_name, ref_name, options = {})
703
- if options[:foreign_key]
704
- to_table = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
705
- remove_foreign_key(table_name, to_table)
924
+ def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
925
+ if foreign_key
926
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
927
+ if foreign_key.is_a?(Hash)
928
+ foreign_key_options = foreign_key
929
+ else
930
+ foreign_key_options = { to_table: reference_name }
931
+ end
932
+ foreign_key_options[:column] ||= "#{ref_name}_id"
933
+ remove_foreign_key(table_name, foreign_key_options)
706
934
  end
707
935
 
708
936
  remove_column(table_name, "#{ref_name}_id")
709
- remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
937
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
710
938
  end
711
939
  alias :remove_belongs_to :remove_reference
712
940
 
713
941
  # Returns an array of foreign keys for the given table.
714
- # The foreign keys are represented as +ForeignKeyDefinition+ objects.
942
+ # The foreign keys are represented as ForeignKeyDefinition objects.
715
943
  def foreign_keys(table_name)
716
944
  raise NotImplementedError, "foreign_keys is not implemented"
717
945
  end
@@ -729,11 +957,11 @@ module ActiveRecord
729
957
  #
730
958
  # generates:
731
959
  #
732
- # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
960
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
733
961
  #
734
962
  # ====== Creating a foreign key on a specific column
735
963
  #
736
- # add_foreign_key :articles, :users, column: :author_id, primary_key: :lng_id
964
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
737
965
  #
738
966
  # generates:
739
967
  #
@@ -745,7 +973,7 @@ module ActiveRecord
745
973
  #
746
974
  # generates:
747
975
  #
748
- # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
976
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
749
977
  #
750
978
  # The +options+ hash can include the following keys:
751
979
  # [<tt>:column</tt>]
@@ -755,28 +983,25 @@ module ActiveRecord
755
983
  # [<tt>:name</tt>]
756
984
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
757
985
  # [<tt>:on_delete</tt>]
758
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
986
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
759
987
  # [<tt>:on_update</tt>]
760
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
988
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
989
+ # [<tt>:validate</tt>]
990
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
761
991
  def add_foreign_key(from_table, to_table, options = {})
762
992
  return unless supports_foreign_keys?
763
993
 
764
- options[:column] ||= foreign_key_column_for(to_table)
765
-
766
- options = {
767
- column: options[:column],
768
- primary_key: options[:primary_key],
769
- name: foreign_key_name(from_table, options),
770
- on_delete: options[:on_delete],
771
- on_update: options[:on_update]
772
- }
994
+ options = foreign_key_options(from_table, to_table, options)
773
995
  at = create_alter_table from_table
774
996
  at.add_foreign_key to_table, options
775
997
 
776
998
  execute schema_creation.accept(at)
777
999
  end
778
1000
 
779
- # Removes the given foreign key from the table.
1001
+ # Removes the given foreign key from the table. Any option parameters provided
1002
+ # will be used to re-add the foreign key in case of a migration rollback.
1003
+ # It is recommended that you provide any options used when creating the foreign
1004
+ # key so that the migration can be reverted properly.
780
1005
  #
781
1006
  # Removes the foreign key on +accounts.branch_id+.
782
1007
  #
@@ -786,28 +1011,22 @@ module ActiveRecord
786
1011
  #
787
1012
  # remove_foreign_key :accounts, column: :owner_id
788
1013
  #
1014
+ # Removes the foreign key on +accounts.owner_id+.
1015
+ #
1016
+ # remove_foreign_key :accounts, to_table: :owners
1017
+ #
789
1018
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
790
1019
  #
791
1020
  # remove_foreign_key :accounts, name: :special_fk_name
792
1021
  #
793
- def remove_foreign_key(from_table, options_or_to_table = {})
1022
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1023
+ # with an addition of
1024
+ # [<tt>:to_table</tt>]
1025
+ # The name of the table that contains the referenced primary key.
1026
+ def remove_foreign_key(from_table, to_table = nil, **options)
794
1027
  return unless supports_foreign_keys?
795
1028
 
796
- if options_or_to_table.is_a?(Hash)
797
- options = options_or_to_table
798
- else
799
- options = { column: foreign_key_column_for(options_or_to_table) }
800
- end
801
-
802
- fk_name_to_delete = options.fetch(:name) do
803
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
804
-
805
- if fk_to_delete
806
- fk_to_delete.name
807
- else
808
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
809
- end
810
- end
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
811
1030
 
812
1031
  at = create_alter_table from_table
813
1032
  at.drop_foreign_key fk_name_to_delete
@@ -815,54 +1034,71 @@ module ActiveRecord
815
1034
  execute schema_creation.accept(at)
816
1035
  end
817
1036
 
1037
+ # Checks to see if a foreign key exists on a table for a given foreign key definition.
1038
+ #
1039
+ # # Checks to see if a foreign key exists.
1040
+ # foreign_key_exists?(:accounts, :branches)
1041
+ #
1042
+ # # Checks to see if a foreign key on a specified column exists.
1043
+ # foreign_key_exists?(:accounts, column: :owner_id)
1044
+ #
1045
+ # # Checks to see if a foreign key with a custom name exists.
1046
+ # foreign_key_exists?(:accounts, name: "special_fk_name")
1047
+ #
1048
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1049
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1050
+ end
1051
+
818
1052
  def foreign_key_column_for(table_name) # :nodoc:
819
- prefix = Base.table_name_prefix
820
- suffix = Base.table_name_suffix
821
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
822
1054
  "#{name.singularize}_id"
823
1055
  end
824
1056
 
825
- def dump_schema_information #:nodoc:
826
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
1057
+ def foreign_key_options(from_table, to_table, options) # :nodoc:
1058
+ options = options.dup
1059
+ options[:column] ||= foreign_key_column_for(to_table)
1060
+ options[:name] ||= foreign_key_name(from_table, options)
1061
+ options
1062
+ end
827
1063
 
828
- ActiveRecord::SchemaMigration.order('version').map { |sm|
829
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
830
- }.join "\n\n"
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1066
+ insert_versions_sql(versions) if versions.any?
831
1067
  end
832
1068
 
833
- # Should not be called normally, but this operation is non-destructive.
834
- # The migrations module handles this automatically.
835
- def initialize_schema_migrations_table
836
- ActiveRecord::SchemaMigration.create_table
1069
+ def internal_string_options_for_primary_key # :nodoc:
1070
+ { primary_key: true }
837
1071
  end
838
1072
 
839
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
840
- migrations_paths = Array(migrations_paths)
1073
+ def assume_migrated_upto_version(version, migrations_paths = nil)
1074
+ unless migrations_paths.nil?
1075
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
1076
+ Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1077
+ MSG
1078
+ end
1079
+
841
1080
  version = version.to_i
842
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
843
1082
 
844
- migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
845
- versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
846
- ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
847
- end
1083
+ migrated = migration_context.get_all_versions
1084
+ versions = migration_context.migrations.map(&:version)
848
1085
 
849
1086
  unless migrated.include?(version)
850
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
1087
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
851
1088
  end
852
1089
 
853
- inserted = Set.new
854
- (versions - migrated).each do |v|
855
- if inserted.include?(v)
856
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
857
- elsif v < version
858
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
859
- inserted << v
1090
+ inserting = (versions - migrated).select { |v| v < version }
1091
+ if inserting.any?
1092
+ if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1093
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
860
1094
  end
1095
+ execute insert_versions_sql(inserting)
861
1096
  end
862
1097
  end
863
1098
 
864
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
865
- if native = native_database_types[type.to_sym]
1099
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
1100
+ type = type.to_sym if type
1101
+ if native = native_database_types[type]
866
1102
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
867
1103
 
868
1104
  if type == :decimal # ignore limit, use precision and scale
@@ -878,6 +1114,12 @@ module ActiveRecord
878
1114
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
879
1115
  end
880
1116
 
1117
+ elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
1118
+ if (0..6) === precision
1119
+ column_type_sql << "(#{precision})"
1120
+ else
1121
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1122
+ end
881
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
882
1124
  column_type_sql << "(#{limit})"
883
1125
  end
@@ -889,7 +1131,7 @@ module ActiveRecord
889
1131
  end
890
1132
 
891
1133
  # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
892
- # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they
1134
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
893
1135
  # require the order columns appear in the SELECT.
894
1136
  #
895
1137
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
@@ -898,14 +1140,18 @@ module ActiveRecord
898
1140
  columns
899
1141
  end
900
1142
 
901
- include TimestampDefaultDeprecation
902
1143
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
903
- # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
1144
+ # Additional options (like +:null+) are forwarded to #add_column.
904
1145
  #
905
- # add_timestamps(:suppliers, null: false)
1146
+ # add_timestamps(:suppliers, null: true)
906
1147
  #
907
1148
  def add_timestamps(table_name, options = {})
908
- emit_warning_if_null_unspecified(:add_timestamps, options)
1149
+ options[:null] = false if options[:null].nil?
1150
+
1151
+ if !options.key?(:precision) && supports_datetime_with_precision?
1152
+ options[:precision] = 6
1153
+ end
1154
+
909
1155
  add_column table_name, :created_at, :datetime, options
910
1156
  add_column table_name, :updated_at, :datetime, options
911
1157
  end
@@ -923,16 +1169,15 @@ module ActiveRecord
923
1169
  Table.new(table_name, base)
924
1170
  end
925
1171
 
926
- def add_index_options(table_name, column_name, options = {}) #:nodoc:
927
- column_names = Array(column_name)
928
- index_name = index_name(table_name, column: column_names)
1172
+ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1173
+ column_names = index_column_names(column_name)
929
1174
 
930
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
1175
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
931
1176
 
932
- index_type = options[:unique] ? "UNIQUE" : ""
933
1177
  index_type = options[:type].to_s if options.key?(:type)
1178
+ index_type ||= options[:unique] ? "UNIQUE" : ""
934
1179
  index_name = options[:name].to_s if options.key?(:name)
935
- max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
1180
+ index_name ||= index_name(table_name, column_names)
936
1181
 
937
1182
  if options.key?(:algorithm)
938
1183
  algorithm = index_algorithms.fetch(options[:algorithm]) {
@@ -946,63 +1191,109 @@ module ActiveRecord
946
1191
  index_options = options[:where] ? " WHERE #{options[:where]}" : ""
947
1192
  end
948
1193
 
949
- if index_name.length > max_index_length
950
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
951
- end
952
- if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
1194
+ validate_index_length!(table_name, index_name, options.fetch(:internal, false))
1195
+
1196
+ if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
953
1197
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
954
1198
  end
955
1199
  index_columns = quoted_columns_for_index(column_names, options).join(", ")
956
1200
 
957
- [index_name, index_type, index_columns, index_options, algorithm, using]
1201
+ [index_name, index_type, index_columns, index_options, algorithm, using, comment]
958
1202
  end
959
1203
 
960
- protected
961
- def add_index_sort_order(option_strings, column_names, options = {})
962
- if options.is_a?(Hash) && order = options[:order]
963
- case order
964
- when Hash
965
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
966
- when String
967
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
968
- end
969
- end
1204
+ def options_include_default?(options)
1205
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1206
+ end
1207
+
1208
+ # Changes the comment for a table or removes it if +nil+.
1209
+ #
1210
+ # Passing a hash containing +:from+ and +:to+ will make this change
1211
+ # reversible in migration:
1212
+ #
1213
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1214
+ def change_table_comment(table_name, comment_or_changes)
1215
+ raise NotImplementedError, "#{self.class} does not support changing table comments"
1216
+ end
1217
+
1218
+ # Changes the comment for a column or removes it if +nil+.
1219
+ #
1220
+ # Passing a hash containing +:from+ and +:to+ will make this change
1221
+ # reversible in migration:
1222
+ #
1223
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1224
+ def change_column_comment(table_name, column_name, comment_or_changes)
1225
+ raise NotImplementedError, "#{self.class} does not support changing column comments"
1226
+ end
1227
+
1228
+ def create_schema_dumper(options) # :nodoc:
1229
+ SchemaDumper.create(self, options)
1230
+ end
970
1231
 
971
- return option_strings
1232
+ private
1233
+ def column_options_keys
1234
+ [:limit, :precision, :scale, :default, :null, :collation, :comment]
1235
+ end
1236
+
1237
+ def add_index_sort_order(quoted_columns, **options)
1238
+ orders = options_for_index_columns(options[:order])
1239
+ quoted_columns.each do |name, column|
1240
+ column << " #{orders[name].upcase}" if orders[name].present?
1241
+ end
972
1242
  end
973
1243
 
974
- # Overridden by the MySQL adapter for supporting index lengths
975
- def quoted_columns_for_index(column_names, options = {})
976
- option_strings = Hash[column_names.map {|name| [name, '']}]
1244
+ def options_for_index_columns(options)
1245
+ if options.is_a?(Hash)
1246
+ options.symbolize_keys
1247
+ else
1248
+ Hash.new { |hash, column| hash[column] = options }
1249
+ end
1250
+ end
977
1251
 
978
- # add index sort order if supported
1252
+ # Overridden by the MySQL adapter for supporting index lengths and by
1253
+ # the PostgreSQL adapter for supporting operator classes.
1254
+ def add_options_for_index_columns(quoted_columns, **options)
979
1255
  if supports_index_sort_order?
980
- option_strings = add_index_sort_order(option_strings, column_names, options)
1256
+ quoted_columns = add_index_sort_order(quoted_columns, options)
981
1257
  end
982
1258
 
983
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
1259
+ quoted_columns
984
1260
  end
985
1261
 
986
- def options_include_default?(options)
987
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1262
+ def quoted_columns_for_index(column_names, **options)
1263
+ return [column_names] if column_names.is_a?(String)
1264
+
1265
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1266
+ add_options_for_index_columns(quoted_columns, options).values
988
1267
  end
989
1268
 
990
1269
  def index_name_for_remove(table_name, options = {})
991
- index_name = index_name(table_name, options)
1270
+ return options[:name] if can_remove_index_by_name?(options)
992
1271
 
993
- unless index_name_exists?(table_name, index_name, true)
994
- if options.is_a?(Hash) && options.has_key?(:name)
995
- options_without_column = options.dup
996
- options_without_column.delete :column
997
- index_name_without_column = index_name(table_name, options_without_column)
1272
+ checks = []
998
1273
 
999
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
1000
- end
1274
+ if options.is_a?(Hash)
1275
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1276
+ column_names = index_column_names(options[:column])
1277
+ else
1278
+ column_names = index_column_names(options)
1279
+ end
1001
1280
 
1002
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
1281
+ if column_names.present?
1282
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1003
1283
  end
1004
1284
 
1005
- index_name
1285
+ raise ArgumentError, "No name or columns specified" if checks.none?
1286
+
1287
+ matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1288
+
1289
+ if matching_indexes.count > 1
1290
+ raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1291
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1292
+ elsif matching_indexes.none?
1293
+ raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1294
+ else
1295
+ matching_indexes.first.name
1296
+ end
1006
1297
  end
1007
1298
 
1008
1299
  def rename_table_indexes(table_name, new_name)
@@ -1027,28 +1318,158 @@ module ActiveRecord
1027
1318
  end
1028
1319
  end
1029
1320
 
1030
- private
1031
- def create_table_definition(name, temporary, options, as = nil)
1032
- TableDefinition.new native_database_types, name, temporary, options, as
1033
- end
1321
+ def schema_creation
1322
+ SchemaCreation.new(self)
1323
+ end
1034
1324
 
1035
- def create_alter_table(name)
1036
- AlterTable.new create_table_definition(name, false, {})
1037
- end
1325
+ def create_table_definition(*args)
1326
+ TableDefinition.new(self, *args)
1327
+ end
1038
1328
 
1039
- def foreign_key_name(table_name, options) # :nodoc:
1040
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1041
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1042
- options.fetch(:name) do
1043
- "fk_rails_#{hashed_identifier}"
1329
+ def create_alter_table(name)
1330
+ AlterTable.new create_table_definition(name)
1044
1331
  end
1045
- end
1046
1332
 
1047
- def validate_index_length!(table_name, new_name)
1048
- if new_name.length > allowed_index_name_length
1049
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1333
+ def fetch_type_metadata(sql_type)
1334
+ cast_type = lookup_cast_type(sql_type)
1335
+ SqlTypeMetadata.new(
1336
+ sql_type: sql_type,
1337
+ type: cast_type.type,
1338
+ limit: cast_type.limit,
1339
+ precision: cast_type.precision,
1340
+ scale: cast_type.scale,
1341
+ )
1342
+ end
1343
+
1344
+ def index_column_names(column_names)
1345
+ if column_names.is_a?(String) && /\W/.match?(column_names)
1346
+ column_names
1347
+ else
1348
+ Array(column_names)
1349
+ end
1350
+ end
1351
+
1352
+ def index_name_options(column_names)
1353
+ if column_names.is_a?(String) && /\W/.match?(column_names)
1354
+ column_names = column_names.scan(/\w+/).join("_")
1355
+ end
1356
+
1357
+ { column: column_names }
1358
+ end
1359
+
1360
+ def strip_table_name_prefix_and_suffix(table_name)
1361
+ prefix = Base.table_name_prefix
1362
+ suffix = Base.table_name_suffix
1363
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1364
+ end
1365
+
1366
+ def foreign_key_name(table_name, options)
1367
+ options.fetch(:name) do
1368
+ identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1369
+ hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1370
+
1371
+ "fk_rails_#{hashed_identifier}"
1372
+ end
1373
+ end
1374
+
1375
+ def foreign_key_for(from_table, **options)
1376
+ return unless supports_foreign_keys?
1377
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1378
+ end
1379
+
1380
+ def foreign_key_for!(from_table, to_table: nil, **options)
1381
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1382
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1383
+ end
1384
+
1385
+ def extract_foreign_key_action(specifier)
1386
+ case specifier
1387
+ when "CASCADE"; :cascade
1388
+ when "SET NULL"; :nullify
1389
+ when "RESTRICT"; :restrict
1390
+ end
1391
+ end
1392
+
1393
+ def validate_index_length!(table_name, new_name, internal = false)
1394
+ max_index_length = internal ? index_name_length : allowed_index_name_length
1395
+
1396
+ if new_name.length > max_index_length
1397
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1398
+ end
1399
+ end
1400
+
1401
+ def extract_new_default_value(default_or_changes)
1402
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1403
+ default_or_changes[:to]
1404
+ else
1405
+ default_or_changes
1406
+ end
1407
+ end
1408
+ alias :extract_new_comment_value :extract_new_default_value
1409
+
1410
+ def can_remove_index_by_name?(options)
1411
+ options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1412
+ end
1413
+
1414
+ def bulk_change_table(table_name, operations)
1415
+ sql_fragments = []
1416
+ non_combinable_operations = []
1417
+
1418
+ operations.each do |command, args|
1419
+ table, arguments = args.shift, args
1420
+ method = :"#{command}_for_alter"
1421
+
1422
+ if respond_to?(method, true)
1423
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1424
+ sql_fragments << sqls
1425
+ non_combinable_operations.concat(procs)
1426
+ else
1427
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1428
+ non_combinable_operations.each(&:call)
1429
+ sql_fragments = []
1430
+ non_combinable_operations = []
1431
+ send(command, table, *arguments)
1432
+ end
1433
+ end
1434
+
1435
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1436
+ non_combinable_operations.each(&:call)
1437
+ end
1438
+
1439
+ def add_column_for_alter(table_name, column_name, type, options = {})
1440
+ td = create_table_definition(table_name)
1441
+ cd = td.new_column_definition(column_name, type, options)
1442
+ schema_creation.accept(AddColumnDefinition.new(cd))
1443
+ end
1444
+
1445
+ def remove_column_for_alter(table_name, column_name, type = nil, options = {})
1446
+ "DROP COLUMN #{quote_column_name(column_name)}"
1447
+ end
1448
+
1449
+ def remove_columns_for_alter(table_name, *column_names)
1450
+ column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1451
+ end
1452
+
1453
+ def insert_versions_sql(versions)
1454
+ sm_table = quote_table_name(schema_migration.table_name)
1455
+
1456
+ if versions.is_a?(Array)
1457
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1458
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1459
+ sql << ";\n\n"
1460
+ sql
1461
+ else
1462
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1463
+ end
1464
+ end
1465
+
1466
+ def data_source_sql(name = nil, type: nil)
1467
+ raise NotImplementedError
1468
+ end
1469
+
1470
+ def quoted_scope(name = nil, type: nil)
1471
+ raise NotImplementedError
1050
1472
  end
1051
- end
1052
1473
  end
1053
1474
  end
1054
1475
  end