activerecord 6.0.0

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

Potentially problematic release.


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

Files changed (340) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1013 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record.rb +195 -0
  8. data/lib/active_record/aggregations.rb +285 -0
  9. data/lib/active_record/association_relation.rb +40 -0
  10. data/lib/active_record/associations.rb +1865 -0
  11. data/lib/active_record/associations/alias_tracker.rb +81 -0
  12. data/lib/active_record/associations/association.rb +332 -0
  13. data/lib/active_record/associations/association_scope.rb +166 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +124 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +36 -0
  16. data/lib/active_record/associations/builder/association.rb +136 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +130 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +72 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +114 -0
  20. data/lib/active_record/associations/builder/has_many.rb +19 -0
  21. data/lib/active_record/associations/builder/has_one.rb +64 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +44 -0
  23. data/lib/active_record/associations/collection_association.rb +498 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1128 -0
  25. data/lib/active_record/associations/foreign_association.rb +20 -0
  26. data/lib/active_record/associations/has_many_association.rb +136 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +220 -0
  28. data/lib/active_record/associations/has_one_association.rb +118 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency.rb +258 -0
  31. data/lib/active_record/associations/join_dependency/join_association.rb +80 -0
  32. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  33. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  34. data/lib/active_record/associations/preloader.rb +201 -0
  35. data/lib/active_record/associations/preloader/association.rb +133 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +116 -0
  37. data/lib/active_record/associations/singular_association.rb +59 -0
  38. data/lib/active_record/associations/through_association.rb +121 -0
  39. data/lib/active_record/attribute_assignment.rb +85 -0
  40. data/lib/active_record/attribute_decorators.rb +90 -0
  41. data/lib/active_record/attribute_methods.rb +420 -0
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +81 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +221 -0
  44. data/lib/active_record/attribute_methods/primary_key.rb +136 -0
  45. data/lib/active_record/attribute_methods/query.rb +41 -0
  46. data/lib/active_record/attribute_methods/read.rb +47 -0
  47. data/lib/active_record/attribute_methods/serialization.rb +90 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +91 -0
  49. data/lib/active_record/attribute_methods/write.rb +61 -0
  50. data/lib/active_record/attributes.rb +279 -0
  51. data/lib/active_record/autosave_association.rb +508 -0
  52. data/lib/active_record/base.rb +328 -0
  53. data/lib/active_record/callbacks.rb +339 -0
  54. data/lib/active_record/coders/json.rb +15 -0
  55. data/lib/active_record/coders/yaml_column.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1165 -0
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +85 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +512 -0
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +154 -0
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +251 -0
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +713 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1475 -0
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +761 -0
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +821 -0
  69. data/lib/active_record/connection_adapters/column.rb +95 -0
  70. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  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 +200 -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 +146 -0
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  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 +41 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +949 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +141 -0
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -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 +557 -0
  127. data/lib/active_record/connection_adapters/statement_pool.rb +61 -0
  128. data/lib/active_record/connection_handling.rb +267 -0
  129. data/lib/active_record/core.rb +599 -0
  130. data/lib/active_record/counter_cache.rb +193 -0
  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 +122 -0
  137. data/lib/active_record/enum.rb +274 -0
  138. data/lib/active_record/errors.rb +388 -0
  139. data/lib/active_record/explain.rb +50 -0
  140. data/lib/active_record/explain_registry.rb +32 -0
  141. data/lib/active_record/explain_subscriber.rb +34 -0
  142. data/lib/active_record/fixture_set/file.rb +82 -0
  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 +738 -0
  148. data/lib/active_record/gem_version.rb +17 -0
  149. data/lib/active_record/inheritance.rb +293 -0
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +207 -0
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  154. data/lib/active_record/locale/en.yml +48 -0
  155. data/lib/active_record/locking/optimistic.rb +197 -0
  156. data/lib/active_record/locking/pessimistic.rb +89 -0
  157. data/lib/active_record/log_subscriber.rb +118 -0
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +1397 -0
  162. data/lib/active_record/migration/command_recorder.rb +284 -0
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +17 -0
  165. data/lib/active_record/model_schema.rb +542 -0
  166. data/lib/active_record/nested_attributes.rb +600 -0
  167. data/lib/active_record/no_touching.rb +65 -0
  168. data/lib/active_record/null_relation.rb +68 -0
  169. data/lib/active_record/persistence.rb +967 -0
  170. data/lib/active_record/query_cache.rb +52 -0
  171. data/lib/active_record/querying.rb +82 -0
  172. data/lib/active_record/railtie.rb +263 -0
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +7 -0
  175. data/lib/active_record/railties/controller_runtime.rb +51 -0
  176. data/lib/active_record/railties/databases.rake +527 -0
  177. data/lib/active_record/readonly_attributes.rb +24 -0
  178. data/lib/active_record/reflection.rb +1042 -0
  179. data/lib/active_record/relation.rb +859 -0
  180. data/lib/active_record/relation/batches.rb +290 -0
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +424 -0
  183. data/lib/active_record/relation/delegation.rb +130 -0
  184. data/lib/active_record/relation/finder_methods.rb +552 -0
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +184 -0
  187. data/lib/active_record/relation/predicate_builder.rb +150 -0
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  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 +19 -0
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +1359 -0
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +77 -0
  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 +168 -0
  202. data/lib/active_record/runtime_registry.rb +24 -0
  203. data/lib/active_record/sanitization.rb +214 -0
  204. data/lib/active_record/schema.rb +61 -0
  205. data/lib/active_record/schema_dumper.rb +270 -0
  206. data/lib/active_record/schema_migration.rb +60 -0
  207. data/lib/active_record/scoping.rb +106 -0
  208. data/lib/active_record/scoping/default.rb +151 -0
  209. data/lib/active_record/scoping/named.rb +217 -0
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +22 -0
  212. data/lib/active_record/statement_cache.rb +148 -0
  213. data/lib/active_record/store.rb +290 -0
  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 +506 -0
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +224 -0
  222. data/lib/active_record/timestamp.rb +167 -0
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +493 -0
  225. data/lib/active_record/translation.rb +24 -0
  226. data/lib/active_record/type.rb +78 -0
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +9 -0
  229. data/lib/active_record/type/date_time.rb +9 -0
  230. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  231. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  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 +71 -0
  235. data/lib/active_record/type/text.rb +11 -0
  236. data/lib/active_record/type/time.rb +21 -0
  237. data/lib/active_record/type/type_map.rb +62 -0
  238. data/lib/active_record/type/unsigned_integer.rb +17 -0
  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 +94 -0
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +60 -0
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +68 -0
  247. data/lib/active_record/validations/uniqueness.rb +226 -0
  248. data/lib/active_record/version.rb +10 -0
  249. data/lib/arel.rb +51 -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 +19 -0
  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 +48 -0
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +75 -0
  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.tt +48 -0
  337. data/lib/rails/generators/active_record/model/model_generator.rb +49 -0
  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.tt +7 -0
  340. metadata +415 -0
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ class SchemaDumper < SchemaDumper # :nodoc:
6
+ def self.create(connection, options)
7
+ new(connection, options)
8
+ end
9
+
10
+ private
11
+ def column_spec(column)
12
+ [schema_type_with_virtual(column), prepare_column_options(column)]
13
+ end
14
+
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
22
+
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
35
+
36
+ def default_primary_key?(column)
37
+ schema_type(column) == :bigint
38
+ end
39
+
40
+ def explicit_primary_key_default?(column)
41
+ false
42
+ end
43
+
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
51
+
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
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,1475 @@
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"
7
+
8
+ module ActiveRecord
9
+ module ConnectionAdapters # :nodoc:
10
+ module SchemaStatements
11
+ include ActiveRecord::Migration::JoinTable
12
+
13
+ # Returns a hash of mappings from the abstract data types to the native
14
+ # database types. See TableDefinition#column for details on the recognized
15
+ # abstract data types.
16
+ def native_database_types
17
+ {}
18
+ end
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
+
29
+ # Truncates a table alias according to the limits of the current adapter.
30
+ def table_alias_for(table_name)
31
+ table_name[0...table_alias_length].tr(".", "_")
32
+ end
33
+
34
+ # Returns the relation names useable to back Active Record models.
35
+ # For most adapters this means all #tables and #views.
36
+ def data_sources
37
+ query_values(data_source_sql, "SCHEMA")
38
+ rescue NotImplementedError
39
+ tables | views
40
+ end
41
+
42
+ # Checks to see if the data source +name+ exists on the database.
43
+ #
44
+ # data_source_exists?(:ebooks)
45
+ #
46
+ def data_source_exists?(name)
47
+ query_values(data_source_sql(name), "SCHEMA").any? if name.present?
48
+ rescue NotImplementedError
49
+ data_sources.include?(name.to_s)
50
+ end
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
+
57
+ # Checks to see if the table +table_name+ exists on the database.
58
+ #
59
+ # table_exists?(:developers)
60
+ #
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
64
+ tables.include?(table_name.to_s)
65
+ end
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
+
82
+ # Returns an array of indexes for the given table.
83
+ def indexes(table_name)
84
+ raise NotImplementedError, "#indexes is not implemented"
85
+ end
86
+
87
+ # Checks to see if an index exists on a table for a given index definition.
88
+ #
89
+ # # Check an index exists
90
+ # index_exists?(:suppliers, :company_id)
91
+ #
92
+ # # Check an index on multiple columns exists
93
+ # index_exists?(:suppliers, [:company_id, :company_type])
94
+ #
95
+ # # Check a unique index exists
96
+ # index_exists?(:suppliers, :company_id, unique: true)
97
+ #
98
+ # # Check an index with a custom name exists
99
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id")
100
+ #
101
+ def index_exists?(table_name, column_name, options = {})
102
+ column_names = Array(column_name).map(&:to_s)
103
+ checks = []
104
+ checks << lambda { |i| Array(i.columns) == column_names }
105
+ checks << lambda { |i| i.unique } if options[:unique]
106
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
+
108
+ indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
109
+ end
110
+
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
118
+
119
+ # Checks to see if a column exists in a given table.
120
+ #
121
+ # # Check a column exists
122
+ # column_exists?(:suppliers, :name)
123
+ #
124
+ # # Check a column exists of a particular type
125
+ # column_exists?(:suppliers, :name, :string)
126
+ #
127
+ # # Check a column exists with a specific definition
128
+ # column_exists?(:suppliers, :name, :string, limit: 100)
129
+ # column_exists?(:suppliers, :name, :string, default: 'default')
130
+ # column_exists?(:suppliers, :name, :string, null: false)
131
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
132
+ #
133
+ def column_exists?(table_name, column_name, type = nil, **options)
134
+ column_name = column_name.to_s
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
150
+ end
151
+
152
+ # Creates a new table with the name +table_name+. +table_name+ may either
153
+ # be a String or a Symbol.
154
+ #
155
+ # There are two ways to work with #create_table. You can use the block
156
+ # form or the regular form, like this:
157
+ #
158
+ # === Block form
159
+ #
160
+ # # create_table() passes a TableDefinition object to the block.
161
+ # # This form will not only create the table, but also columns for the
162
+ # # table.
163
+ #
164
+ # create_table(:suppliers) do |t|
165
+ # t.column :name, :string, limit: 60
166
+ # # Other fields here
167
+ # end
168
+ #
169
+ # === Block form, with shorthand
170
+ #
171
+ # # You can also use the column types as method calls, rather than calling the column method.
172
+ # create_table(:suppliers) do |t|
173
+ # t.string :name, limit: 60
174
+ # # Other fields here
175
+ # end
176
+ #
177
+ # === Regular form
178
+ #
179
+ # # Creates a table called 'suppliers' with no columns.
180
+ # create_table(:suppliers)
181
+ # # Add a column to 'suppliers'.
182
+ # add_column(:suppliers, :name, :string, {limit: 60})
183
+ #
184
+ # The +options+ hash can include the following keys:
185
+ # [<tt>:id</tt>]
186
+ # Whether to automatically add a primary key column. Defaults to true.
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.
190
+ # [<tt>:primary_key</tt>]
191
+ # The name of the primary key, if one is to be added automatically.
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.
195
+ #
196
+ # Note that Active Record models will automatically detect their
197
+ # primary key. This can be avoided by using
198
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
199
+ # to define the key explicitly.
200
+ #
201
+ # [<tt>:options</tt>]
202
+ # Any extra options you want appended to the table definition.
203
+ # [<tt>:temporary</tt>]
204
+ # Make a temporary table.
205
+ # [<tt>:force</tt>]
206
+ # Set to true to drop the table before creating it.
207
+ # Set to +:cascade+ to drop dependent objects as well.
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.
212
+ # [<tt>:as</tt>]
213
+ # SQL to use to generate the table. When this option is used, the block is
214
+ # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
215
+ #
216
+ # ====== Add a backend specific option to the generated SQL (MySQL)
217
+ #
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
219
+ #
220
+ # generates:
221
+ #
222
+ # CREATE TABLE suppliers (
223
+ # id bigint auto_increment PRIMARY KEY
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
225
+ #
226
+ # ====== Rename the primary key column
227
+ #
228
+ # create_table(:objects, primary_key: 'guid') do |t|
229
+ # t.column :name, :string, limit: 80
230
+ # end
231
+ #
232
+ # generates:
233
+ #
234
+ # CREATE TABLE objects (
235
+ # guid bigint auto_increment PRIMARY KEY,
236
+ # name varchar(80)
237
+ # )
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
+ #
269
+ # ====== Do not add a primary key column
270
+ #
271
+ # create_table(:categories_suppliers, id: false) do |t|
272
+ # t.column :category_id, :bigint
273
+ # t.column :supplier_id, :bigint
274
+ # end
275
+ #
276
+ # generates:
277
+ #
278
+ # CREATE TABLE categories_suppliers (
279
+ # category_id bigint,
280
+ # supplier_id bigint
281
+ # )
282
+ #
283
+ # ====== Create a temporary table based on a query
284
+ #
285
+ # create_table(:long_query, temporary: true,
286
+ # as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
287
+ #
288
+ # generates:
289
+ #
290
+ # CREATE TEMPORARY TABLE long_query AS
291
+ # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
292
+ #
293
+ # See also TableDefinition#column for details on how to create columns.
294
+ def create_table(table_name, **options)
295
+ td = create_table_definition(table_name, options)
296
+
297
+ if options[:id] != false && !options[:as]
298
+ pk = options.fetch(:primary_key) do
299
+ Base.get_primary_key table_name.to_s.singularize
300
+ end
301
+
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
307
+ end
308
+
309
+ yield td if block_given?
310
+
311
+ if options[:force]
312
+ drop_table(table_name, options.merge(if_exists: true))
313
+ end
314
+
315
+ result = execute schema_creation.accept td
316
+
317
+ unless supports_indexes_in_create?
318
+ td.indexes.each do |column_name, index_options|
319
+ add_index(table_name, column_name, index_options)
320
+ end
321
+ end
322
+
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
331
+ end
332
+
333
+ result
334
+ end
335
+
336
+ # Creates a new join table with the name created using the lexical order of the first two
337
+ # arguments. These arguments can be a String or a Symbol.
338
+ #
339
+ # # Creates a table called 'assemblies_parts' with no id.
340
+ # create_join_table(:assemblies, :parts)
341
+ #
342
+ # You can pass an +options+ hash which can include the following keys:
343
+ # [<tt>:table_name</tt>]
344
+ # Sets the table name, overriding the default.
345
+ # [<tt>:column_options</tt>]
346
+ # Any extra options you want appended to the columns definition.
347
+ # [<tt>:options</tt>]
348
+ # Any extra options you want appended to the table definition.
349
+ # [<tt>:temporary</tt>]
350
+ # Make a temporary table.
351
+ # [<tt>:force</tt>]
352
+ # Set to true to drop the table before creating it.
353
+ # Defaults to false.
354
+ #
355
+ # Note that #create_join_table does not create any indices by default; you can use
356
+ # its block form to do so yourself:
357
+ #
358
+ # create_join_table :products, :categories do |t|
359
+ # t.index :product_id
360
+ # t.index :category_id
361
+ # end
362
+ #
363
+ # ====== Add a backend specific option to the generated SQL (MySQL)
364
+ #
365
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
366
+ #
367
+ # generates:
368
+ #
369
+ # CREATE TABLE assemblies_parts (
370
+ # assembly_id bigint NOT NULL,
371
+ # part_id bigint NOT NULL,
372
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
373
+ #
374
+ def create_join_table(table_1, table_2, column_options: {}, **options)
375
+ join_table_name = find_join_table_name(table_1, table_2, options)
376
+
377
+ column_options.reverse_merge!(null: false, index: false)
378
+
379
+ t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
380
+
381
+ create_table(join_table_name, options.merge!(id: false)) do |td|
382
+ td.references t1_ref, column_options
383
+ td.references t2_ref, column_options
384
+ yield td if block_given?
385
+ end
386
+ end
387
+
388
+ # Drops the join table specified by the given arguments.
389
+ # See #create_join_table for details.
390
+ #
391
+ # Although this command ignores the block if one is given, it can be helpful
392
+ # to provide one in a migration's +change+ method so it can be reverted.
393
+ # In that case, the block will be used by #create_join_table.
394
+ def drop_join_table(table_1, table_2, options = {})
395
+ join_table_name = find_join_table_name(table_1, table_2, options)
396
+ drop_table(join_table_name)
397
+ end
398
+
399
+ # A block for changing columns in +table+.
400
+ #
401
+ # # change_table() yields a Table instance
402
+ # change_table(:suppliers) do |t|
403
+ # t.column :name, :string, limit: 60
404
+ # # Other column alterations here
405
+ # end
406
+ #
407
+ # The +options+ hash can include the following keys:
408
+ # [<tt>:bulk</tt>]
409
+ # Set this to true to make this a bulk alter query, such as
410
+ #
411
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
412
+ #
413
+ # Defaults to false.
414
+ #
415
+ # Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
416
+ #
417
+ # ====== Add a column
418
+ #
419
+ # change_table(:suppliers) do |t|
420
+ # t.column :name, :string, limit: 60
421
+ # end
422
+ #
423
+ # ====== Add 2 integer columns
424
+ #
425
+ # change_table(:suppliers) do |t|
426
+ # t.integer :width, :height, null: false, default: 0
427
+ # end
428
+ #
429
+ # ====== Add created_at/updated_at columns
430
+ #
431
+ # change_table(:suppliers) do |t|
432
+ # t.timestamps
433
+ # end
434
+ #
435
+ # ====== Add a foreign key column
436
+ #
437
+ # change_table(:suppliers) do |t|
438
+ # t.references :company
439
+ # end
440
+ #
441
+ # Creates a <tt>company_id(bigint)</tt> column.
442
+ #
443
+ # ====== Add a polymorphic foreign key column
444
+ #
445
+ # change_table(:suppliers) do |t|
446
+ # t.belongs_to :company, polymorphic: true
447
+ # end
448
+ #
449
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
450
+ #
451
+ # ====== Remove a column
452
+ #
453
+ # change_table(:suppliers) do |t|
454
+ # t.remove :company
455
+ # end
456
+ #
457
+ # ====== Remove several columns
458
+ #
459
+ # change_table(:suppliers) do |t|
460
+ # t.remove :company_id
461
+ # t.remove :width, :height
462
+ # end
463
+ #
464
+ # ====== Remove an index
465
+ #
466
+ # change_table(:suppliers) do |t|
467
+ # t.remove_index :company_id
468
+ # end
469
+ #
470
+ # See also Table for details on all of the various column transformations.
471
+ def change_table(table_name, options = {})
472
+ if supports_bulk_alter? && options[:bulk]
473
+ recorder = ActiveRecord::Migration::CommandRecorder.new(self)
474
+ yield update_table_definition(table_name, recorder)
475
+ bulk_change_table(table_name, recorder.commands)
476
+ else
477
+ yield update_table_definition(table_name, self)
478
+ end
479
+ end
480
+
481
+ # Renames a table.
482
+ #
483
+ # rename_table('octopuses', 'octopi')
484
+ #
485
+ def rename_table(table_name, new_name)
486
+ raise NotImplementedError, "rename_table is not implemented"
487
+ end
488
+
489
+ # Drops a table from the database.
490
+ #
491
+ # [<tt>:force</tt>]
492
+ # Set to +:cascade+ to drop dependent objects as well.
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.
497
+ #
498
+ # Although this command ignores most +options+ and the block if one is given,
499
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
500
+ # In that case, +options+ and the block will be used by #create_table.
501
+ def drop_table(table_name, options = {})
502
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
503
+ end
504
+
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)
589
+ at = create_alter_table table_name
590
+ at.add_column(column_name, type, options)
591
+ execute schema_creation.accept at
592
+ end
593
+
594
+ # Removes the given columns from the table definition.
595
+ #
596
+ # remove_columns(:suppliers, :qualification, :experience)
597
+ #
598
+ def remove_columns(table_name, *column_names)
599
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
600
+ column_names.each do |column_name|
601
+ remove_column(table_name, column_name)
602
+ end
603
+ end
604
+
605
+ # Removes the column from the table definition.
606
+ #
607
+ # remove_column(:suppliers, :qualification)
608
+ #
609
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
610
+ # to provide these in a migration's +change+ method so it can be reverted.
611
+ # In that case, +type+ and +options+ will be used by #add_column.
612
+ # Indexes on the column are automatically removed.
613
+ def remove_column(table_name, column_name, type = nil, options = {})
614
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
615
+ end
616
+
617
+ # Changes the column's definition according to the new options.
618
+ # See TableDefinition#column for details of the options you can use.
619
+ #
620
+ # change_column(:suppliers, :name, :string, limit: 80)
621
+ # change_column(:accounts, :description, :text)
622
+ #
623
+ def change_column(table_name, column_name, type, options = {})
624
+ raise NotImplementedError, "change_column is not implemented"
625
+ end
626
+
627
+ # Sets a new default value for a column:
628
+ #
629
+ # change_column_default(:suppliers, :qualification, 'new')
630
+ # change_column_default(:accounts, :authorized, 1)
631
+ #
632
+ # Setting the default to +nil+ effectively drops the default:
633
+ #
634
+ # change_column_default(:users, :email, nil)
635
+ #
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)
642
+ raise NotImplementedError, "change_column_default is not implemented"
643
+ end
644
+
645
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
646
+ # indicates whether the value can be +NULL+. For example
647
+ #
648
+ # change_column_null(:users, :nickname, false)
649
+ #
650
+ # says nicknames cannot be +NULL+ (adds the constraint), whereas
651
+ #
652
+ # change_column_null(:users, :nickname, true)
653
+ #
654
+ # allows them to be +NULL+ (drops the constraint).
655
+ #
656
+ # The method accepts an optional fourth argument to replace existing
657
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
658
+ # constraint if needed, since otherwise those rows would not be valid.
659
+ #
660
+ # Please note the fourth argument does not set a column's default.
661
+ def change_column_null(table_name, column_name, null, default = nil)
662
+ raise NotImplementedError, "change_column_null is not implemented"
663
+ end
664
+
665
+ # Renames a column.
666
+ #
667
+ # rename_column(:suppliers, :description, :name)
668
+ #
669
+ def rename_column(table_name, column_name, new_column_name)
670
+ raise NotImplementedError, "rename_column is not implemented"
671
+ end
672
+
673
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
674
+ # an Array of Symbols.
675
+ #
676
+ # The index will be named after the table and the column name(s), unless
677
+ # you pass <tt>:name</tt> as an option.
678
+ #
679
+ # ====== Creating a simple index
680
+ #
681
+ # add_index(:suppliers, :name)
682
+ #
683
+ # generates:
684
+ #
685
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
686
+ #
687
+ # ====== Creating a unique index
688
+ #
689
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
690
+ #
691
+ # generates:
692
+ #
693
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
694
+ #
695
+ # ====== Creating a named index
696
+ #
697
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
698
+ #
699
+ # generates:
700
+ #
701
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
702
+ #
703
+ # ====== Creating an index with specific key length
704
+ #
705
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
706
+ #
707
+ # generates:
708
+ #
709
+ # CREATE INDEX by_name ON accounts(name(10))
710
+ #
711
+ # ====== Creating an index with specific key lengths for multiple keys
712
+ #
713
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
714
+ #
715
+ # generates:
716
+ #
717
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
718
+ #
719
+ # Note: SQLite doesn't support index length.
720
+ #
721
+ # ====== Creating an index with a sort order (desc or asc, asc is the default)
722
+ #
723
+ # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
724
+ #
725
+ # generates:
726
+ #
727
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
728
+ #
729
+ # Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
730
+ #
731
+ # ====== Creating a partial index
732
+ #
733
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
734
+ #
735
+ # generates:
736
+ #
737
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
738
+ #
739
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
740
+ #
741
+ # ====== Creating an index with a specific method
742
+ #
743
+ # add_index(:developers, :name, using: 'btree')
744
+ #
745
+ # generates:
746
+ #
747
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
748
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
749
+ #
750
+ # Note: only supported by PostgreSQL and MySQL
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
+ #
765
+ # ====== Creating an index with a specific type
766
+ #
767
+ # add_index(:developers, :name, type: :fulltext)
768
+ #
769
+ # generates:
770
+ #
771
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
772
+ #
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].
785
+ def add_index(table_name, column_name, options = {})
786
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
787
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
788
+ end
789
+
790
+ # Removes the given index from the table.
791
+ #
792
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
793
+ #
794
+ # remove_index :accounts, :branch_id
795
+ #
796
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
797
+ #
798
+ # remove_index :accounts, column: :branch_id
799
+ #
800
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
801
+ #
802
+ # remove_index :accounts, column: [:branch_id, :party_id]
803
+ #
804
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
805
+ #
806
+ # remove_index :accounts, name: :by_branch_party
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].
817
+ def remove_index(table_name, options = {})
818
+ index_name = index_name_for_remove(table_name, options)
819
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
820
+ end
821
+
822
+ # Renames an index.
823
+ #
824
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
825
+ #
826
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
827
+ #
828
+ def rename_index(table_name, old_name, new_name)
829
+ validate_index_length!(table_name, new_name)
830
+
831
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
832
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
833
+ return unless old_index_def
834
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
835
+ remove_index(table_name, name: old_name)
836
+ end
837
+
838
+ def index_name(table_name, options) #:nodoc:
839
+ if Hash === options
840
+ if options[:column]
841
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
842
+ elsif options[:name]
843
+ options[:name]
844
+ else
845
+ raise ArgumentError, "You must specify the index name"
846
+ end
847
+ else
848
+ index_name(table_name, index_name_options(options))
849
+ end
850
+ end
851
+
852
+ # Verifies the existence of an index with a given name.
853
+ def index_name_exists?(table_name, index_name)
854
+ index_name = index_name.to_s
855
+ indexes(table_name).detect { |i| i.name == index_name }
856
+ end
857
+
858
+ # Adds a reference. The reference column is a bigint by default,
859
+ # the <tt>:type</tt> option can be used to specify a different type.
860
+ # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
861
+ # #add_reference and #add_belongs_to are acceptable.
862
+ #
863
+ # The +options+ hash can include the following keys:
864
+ # [<tt>:type</tt>]
865
+ # The reference column type. Defaults to +:bigint+.
866
+ # [<tt>:index</tt>]
867
+ # Add an appropriate index. Defaults to true.
868
+ # See #add_index for usage of this option.
869
+ # [<tt>:foreign_key</tt>]
870
+ # Add an appropriate foreign key constraint. Defaults to false.
871
+ # [<tt>:polymorphic</tt>]
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.
875
+ #
876
+ # ====== Create a user_id bigint column without an index
877
+ #
878
+ # add_reference(:products, :user, index: false)
879
+ #
880
+ # ====== Create a user_id string column
881
+ #
882
+ # add_reference(:products, :user, type: :string)
883
+ #
884
+ # ====== Create supplier_id, supplier_type columns
885
+ #
886
+ # add_reference(:products, :supplier, polymorphic: true)
887
+ #
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))
906
+ end
907
+ alias :add_belongs_to :add_reference
908
+
909
+ # Removes the reference(s). Also removes a +type+ column if one exists.
910
+ # #remove_reference and #remove_belongs_to are acceptable.
911
+ #
912
+ # ====== Remove the reference
913
+ #
914
+ # remove_reference(:products, :user, index: false)
915
+ #
916
+ # ====== Remove polymorphic reference
917
+ #
918
+ # remove_reference(:products, :supplier, polymorphic: true)
919
+ #
920
+ # ====== Remove the reference with a foreign key
921
+ #
922
+ # remove_reference(:products, :user, foreign_key: true)
923
+ #
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)
934
+ end
935
+
936
+ remove_column(table_name, "#{ref_name}_id")
937
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
938
+ end
939
+ alias :remove_belongs_to :remove_reference
940
+
941
+ # Returns an array of foreign keys for the given table.
942
+ # The foreign keys are represented as ForeignKeyDefinition objects.
943
+ def foreign_keys(table_name)
944
+ raise NotImplementedError, "foreign_keys is not implemented"
945
+ end
946
+
947
+ # Adds a new foreign key. +from_table+ is the table with the key column,
948
+ # +to_table+ contains the referenced primary key.
949
+ #
950
+ # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
951
+ # +identifier+ is a 10 character long string which is deterministically generated from the
952
+ # +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
953
+ #
954
+ # ====== Creating a simple foreign key
955
+ #
956
+ # add_foreign_key :articles, :authors
957
+ #
958
+ # generates:
959
+ #
960
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
961
+ #
962
+ # ====== Creating a foreign key on a specific column
963
+ #
964
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
965
+ #
966
+ # generates:
967
+ #
968
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
969
+ #
970
+ # ====== Creating a cascading foreign key
971
+ #
972
+ # add_foreign_key :articles, :authors, on_delete: :cascade
973
+ #
974
+ # generates:
975
+ #
976
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
977
+ #
978
+ # The +options+ hash can include the following keys:
979
+ # [<tt>:column</tt>]
980
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
981
+ # [<tt>:primary_key</tt>]
982
+ # The primary key column name on +to_table+. Defaults to +id+.
983
+ # [<tt>:name</tt>]
984
+ # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
985
+ # [<tt>:on_delete</tt>]
986
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
987
+ # [<tt>:on_update</tt>]
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+.
991
+ def add_foreign_key(from_table, to_table, options = {})
992
+ return unless supports_foreign_keys?
993
+
994
+ options = foreign_key_options(from_table, to_table, options)
995
+ at = create_alter_table from_table
996
+ at.add_foreign_key to_table, options
997
+
998
+ execute schema_creation.accept(at)
999
+ end
1000
+
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.
1005
+ #
1006
+ # Removes the foreign key on +accounts.branch_id+.
1007
+ #
1008
+ # remove_foreign_key :accounts, :branches
1009
+ #
1010
+ # Removes the foreign key on +accounts.owner_id+.
1011
+ #
1012
+ # remove_foreign_key :accounts, column: :owner_id
1013
+ #
1014
+ # Removes the foreign key on +accounts.owner_id+.
1015
+ #
1016
+ # remove_foreign_key :accounts, to_table: :owners
1017
+ #
1018
+ # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
1019
+ #
1020
+ # remove_foreign_key :accounts, name: :special_fk_name
1021
+ #
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)
1027
+ return unless supports_foreign_keys?
1028
+
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1030
+
1031
+ at = create_alter_table from_table
1032
+ at.drop_foreign_key fk_name_to_delete
1033
+
1034
+ execute schema_creation.accept(at)
1035
+ end
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
+
1052
+ def foreign_key_column_for(table_name) # :nodoc:
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
1054
+ "#{name.singularize}_id"
1055
+ end
1056
+
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
1063
+
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1066
+ insert_versions_sql(versions) if versions.any?
1067
+ end
1068
+
1069
+ def internal_string_options_for_primary_key # :nodoc:
1070
+ { primary_key: true }
1071
+ end
1072
+
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
+
1080
+ version = version.to_i
1081
+ sm_table = quote_table_name(schema_migration.table_name)
1082
+
1083
+ migrated = migration_context.get_all_versions
1084
+ versions = migration_context.migrations.map(&:version)
1085
+
1086
+ unless migrated.include?(version)
1087
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
1088
+ end
1089
+
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."
1094
+ end
1095
+ execute insert_versions_sql(inserting)
1096
+ end
1097
+ end
1098
+
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]
1102
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
1103
+
1104
+ if type == :decimal # ignore limit, use precision and scale
1105
+ scale ||= native[:scale]
1106
+
1107
+ if precision ||= native[:precision]
1108
+ if scale
1109
+ column_type_sql << "(#{precision},#{scale})"
1110
+ else
1111
+ column_type_sql << "(#{precision})"
1112
+ end
1113
+ elsif scale
1114
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
1115
+ end
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
1123
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1124
+ column_type_sql << "(#{limit})"
1125
+ end
1126
+
1127
+ column_type_sql
1128
+ else
1129
+ type.to_s
1130
+ end
1131
+ end
1132
+
1133
+ # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
1134
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
1135
+ # require the order columns appear in the SELECT.
1136
+ #
1137
+ # columns_for_distinct("posts.id", ["posts.created_at desc"])
1138
+ #
1139
+ def columns_for_distinct(columns, orders) # :nodoc:
1140
+ columns
1141
+ end
1142
+
1143
+ # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1144
+ # Additional options (like +:null+) are forwarded to #add_column.
1145
+ #
1146
+ # add_timestamps(:suppliers, null: true)
1147
+ #
1148
+ def add_timestamps(table_name, 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
+
1155
+ add_column table_name, :created_at, :datetime, options
1156
+ add_column table_name, :updated_at, :datetime, options
1157
+ end
1158
+
1159
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1160
+ #
1161
+ # remove_timestamps(:suppliers)
1162
+ #
1163
+ def remove_timestamps(table_name, options = {})
1164
+ remove_column table_name, :updated_at
1165
+ remove_column table_name, :created_at
1166
+ end
1167
+
1168
+ def update_table_definition(table_name, base) #:nodoc:
1169
+ Table.new(table_name, base)
1170
+ end
1171
+
1172
+ def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1173
+ column_names = index_column_names(column_name)
1174
+
1175
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
1176
+
1177
+ index_type = options[:type].to_s if options.key?(:type)
1178
+ index_type ||= options[:unique] ? "UNIQUE" : ""
1179
+ index_name = options[:name].to_s if options.key?(:name)
1180
+ index_name ||= index_name(table_name, column_names)
1181
+
1182
+ if options.key?(:algorithm)
1183
+ algorithm = index_algorithms.fetch(options[:algorithm]) {
1184
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
1185
+ }
1186
+ end
1187
+
1188
+ using = "USING #{options[:using]}" if options[:using].present?
1189
+
1190
+ if supports_partial_index?
1191
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
1192
+ end
1193
+
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)
1197
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
1198
+ end
1199
+ index_columns = quoted_columns_for_index(column_names, options).join(", ")
1200
+
1201
+ [index_name, index_type, index_columns, index_options, algorithm, using, comment]
1202
+ end
1203
+
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
1231
+
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
1242
+ end
1243
+
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
1251
+
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)
1255
+ if supports_index_sort_order?
1256
+ quoted_columns = add_index_sort_order(quoted_columns, options)
1257
+ end
1258
+
1259
+ quoted_columns
1260
+ end
1261
+
1262
+ def quoted_columns_for_index(column_names, **options)
1263
+ return [column_names] if column_names.is_a?(String)
1264
+
1265
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1266
+ add_options_for_index_columns(quoted_columns, options).values
1267
+ end
1268
+
1269
+ def index_name_for_remove(table_name, options = {})
1270
+ return options[:name] if can_remove_index_by_name?(options)
1271
+
1272
+ checks = []
1273
+
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
1280
+
1281
+ if column_names.present?
1282
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1283
+ end
1284
+
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
1297
+ end
1298
+
1299
+ def rename_table_indexes(table_name, new_name)
1300
+ indexes(new_name).each do |index|
1301
+ generated_index_name = index_name(table_name, column: index.columns)
1302
+ if generated_index_name == index.name
1303
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
1304
+ end
1305
+ end
1306
+ end
1307
+
1308
+ def rename_column_indexes(table_name, column_name, new_column_name)
1309
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
1310
+ indexes(table_name).each do |index|
1311
+ next unless index.columns.include?(new_column_name)
1312
+ old_columns = index.columns.dup
1313
+ old_columns[old_columns.index(new_column_name)] = column_name
1314
+ generated_index_name = index_name(table_name, column: old_columns)
1315
+ if generated_index_name == index.name
1316
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
1317
+ end
1318
+ end
1319
+ end
1320
+
1321
+ def schema_creation
1322
+ SchemaCreation.new(self)
1323
+ end
1324
+
1325
+ def create_table_definition(*args)
1326
+ TableDefinition.new(self, *args)
1327
+ end
1328
+
1329
+ def create_alter_table(name)
1330
+ AlterTable.new create_table_definition(name)
1331
+ end
1332
+
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
1472
+ end
1473
+ end
1474
+ end
1475
+ end