activerecord 4.2.0 → 6.0.5.1

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

Potentially problematic release.


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

Files changed (373) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +852 -801
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +14 -13
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +267 -249
  9. data/lib/active_record/association_relation.rb +26 -6
  10. data/lib/active_record/associations/alias_tracker.rb +29 -36
  11. data/lib/active_record/associations/association.rb +137 -55
  12. data/lib/active_record/associations/association_scope.rb +110 -132
  13. data/lib/active_record/associations/belongs_to_association.rb +67 -54
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  15. data/lib/active_record/associations/builder/association.rb +27 -40
  16. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  17. data/lib/active_record/associations/builder/collection_association.rb +10 -29
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +58 -70
  19. data/lib/active_record/associations/builder/has_many.rb +8 -4
  20. data/lib/active_record/associations/builder/has_one.rb +46 -5
  21. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  22. data/lib/active_record/associations/collection_association.rb +150 -275
  23. data/lib/active_record/associations/collection_proxy.rb +253 -152
  24. data/lib/active_record/associations/foreign_association.rb +20 -0
  25. data/lib/active_record/associations/has_many_association.rb +35 -84
  26. data/lib/active_record/associations/has_many_through_association.rb +62 -80
  27. data/lib/active_record/associations/has_one_association.rb +62 -49
  28. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  29. data/lib/active_record/associations/join_dependency/join_association.rb +43 -78
  30. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  31. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  32. data/lib/active_record/associations/join_dependency.rb +159 -162
  33. data/lib/active_record/associations/preloader/association.rb +102 -113
  34. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  35. data/lib/active_record/associations/preloader.rb +96 -95
  36. data/lib/active_record/associations/singular_association.rb +18 -45
  37. data/lib/active_record/associations/through_association.rb +49 -24
  38. data/lib/active_record/associations.rb +1737 -1596
  39. data/lib/active_record/attribute_assignment.rb +57 -185
  40. data/lib/active_record/attribute_decorators.rb +39 -17
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +14 -5
  42. data/lib/active_record/attribute_methods/dirty.rb +174 -134
  43. data/lib/active_record/attribute_methods/primary_key.rb +90 -84
  44. data/lib/active_record/attribute_methods/query.rb +6 -5
  45. data/lib/active_record/attribute_methods/read.rb +20 -77
  46. data/lib/active_record/attribute_methods/serialization.rb +40 -21
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -37
  48. data/lib/active_record/attribute_methods/write.rb +33 -56
  49. data/lib/active_record/attribute_methods.rb +124 -143
  50. data/lib/active_record/attributes.rb +213 -74
  51. data/lib/active_record/autosave_association.rb +125 -54
  52. data/lib/active_record/base.rb +60 -49
  53. data/lib/active_record/callbacks.rb +101 -76
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +36 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +810 -291
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +253 -108
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -24
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +171 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -47
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +383 -239
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +736 -235
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +190 -87
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -192
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +536 -600
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -153
  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 +196 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +71 -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 +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -196
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -5
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  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 +49 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +465 -291
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +565 -363
  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 +119 -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 +102 -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 +299 -364
  127. data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
  128. data/lib/active_record/connection_handling.rb +167 -41
  129. data/lib/active_record/core.rb +277 -233
  130. data/lib/active_record/counter_cache.rb +71 -50
  131. data/lib/active_record/database_configurations/database_config.rb +37 -0
  132. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  133. data/lib/active_record/database_configurations/url_config.rb +78 -0
  134. data/lib/active_record/database_configurations.rb +233 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -106
  137. data/lib/active_record/enum.rb +172 -89
  138. data/lib/active_record/errors.rb +189 -53
  139. data/lib/active_record/explain.rb +22 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +11 -6
  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 +152 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  147. data/lib/active_record/fixtures.rb +225 -497
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -115
  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 +48 -0
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +99 -98
  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/resolver/session.rb +45 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  160. data/lib/active_record/middleware/database_selector.rb +74 -0
  161. data/lib/active_record/migration/command_recorder.rb +166 -91
  162. data/lib/active_record/migration/compatibility.rb +244 -0
  163. data/lib/active_record/migration/join_table.rb +8 -7
  164. data/lib/active_record/migration.rb +636 -290
  165. data/lib/active_record/model_schema.rb +344 -112
  166. data/lib/active_record/nested_attributes.rb +265 -215
  167. data/lib/active_record/no_touching.rb +15 -2
  168. data/lib/active_record/null_relation.rb +24 -38
  169. data/lib/active_record/persistence.rb +559 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +44 -30
  172. data/lib/active_record/railtie.rb +166 -47
  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 +341 -202
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +461 -302
  179. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  180. data/lib/active_record/relation/batches.rb +206 -55
  181. data/lib/active_record/relation/calculations.rb +270 -249
  182. data/lib/active_record/relation/delegation.rb +76 -84
  183. data/lib/active_record/relation/finder_methods.rb +287 -255
  184. data/lib/active_record/relation/from_clause.rb +30 -0
  185. data/lib/active_record/relation/merger.rb +86 -68
  186. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -25
  187. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  188. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  189. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  190. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  191. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  192. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  193. data/lib/active_record/relation/predicate_builder.rb +112 -92
  194. data/lib/active_record/relation/query_attribute.rb +50 -0
  195. data/lib/active_record/relation/query_methods.rb +612 -392
  196. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  197. data/lib/active_record/relation/spawn_methods.rb +18 -17
  198. data/lib/active_record/relation/where_clause.rb +189 -0
  199. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  200. data/lib/active_record/relation.rb +533 -340
  201. data/lib/active_record/result.rb +79 -43
  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 -20
  207. data/lib/active_record/scoping/default.rb +98 -82
  208. data/lib/active_record/scoping/named.rb +91 -33
  209. data/lib/active_record/scoping.rb +45 -27
  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 +90 -0
  216. data/lib/active_record/tasks/database_tasks.rb +309 -99
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +58 -89
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -31
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +37 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +243 -0
  222. data/lib/active_record/timestamp.rb +86 -41
  223. data/lib/active_record/touch_later.rb +65 -0
  224. data/lib/active_record/transactions.rb +222 -146
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type/adapter_specific_registry.rb +126 -0
  227. data/lib/active_record/type/date.rb +4 -41
  228. data/lib/active_record/type/date_time.rb +4 -38
  229. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  230. data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
  231. data/lib/active_record/type/internal/timezone.rb +17 -0
  232. data/lib/active_record/type/json.rb +30 -0
  233. data/lib/active_record/type/serialized.rb +29 -15
  234. data/lib/active_record/type/text.rb +2 -2
  235. data/lib/active_record/type/time.rb +21 -16
  236. data/lib/active_record/type/type_map.rb +16 -19
  237. data/lib/active_record/type/unsigned_integer.rb +9 -8
  238. data/lib/active_record/type.rb +77 -23
  239. data/lib/active_record/type_caster/connection.rb +34 -0
  240. data/lib/active_record/type_caster/map.rb +20 -0
  241. data/lib/active_record/type_caster.rb +9 -0
  242. data/lib/active_record/validations/absence.rb +25 -0
  243. data/lib/active_record/validations/associated.rb +12 -4
  244. data/lib/active_record/validations/length.rb +26 -0
  245. data/lib/active_record/validations/presence.rb +14 -13
  246. data/lib/active_record/validations/uniqueness.rb +43 -46
  247. data/lib/active_record/validations.rb +38 -35
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/active_record.rb +44 -21
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes/attribute.rb +37 -0
  252. data/lib/arel/attributes.rb +22 -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/and.rb +32 -0
  266. data/lib/arel/nodes/ascending.rb +23 -0
  267. data/lib/arel/nodes/binary.rb +52 -0
  268. data/lib/arel/nodes/bind_param.rb +36 -0
  269. data/lib/arel/nodes/case.rb +55 -0
  270. data/lib/arel/nodes/casted.rb +50 -0
  271. data/lib/arel/nodes/comment.rb +29 -0
  272. data/lib/arel/nodes/count.rb +12 -0
  273. data/lib/arel/nodes/delete_statement.rb +45 -0
  274. data/lib/arel/nodes/descending.rb +23 -0
  275. data/lib/arel/nodes/equality.rb +18 -0
  276. data/lib/arel/nodes/extract.rb +24 -0
  277. data/lib/arel/nodes/false.rb +16 -0
  278. data/lib/arel/nodes/full_outer_join.rb +8 -0
  279. data/lib/arel/nodes/function.rb +44 -0
  280. data/lib/arel/nodes/grouping.rb +8 -0
  281. data/lib/arel/nodes/in.rb +8 -0
  282. data/lib/arel/nodes/infix_operation.rb +80 -0
  283. data/lib/arel/nodes/inner_join.rb +8 -0
  284. data/lib/arel/nodes/insert_statement.rb +37 -0
  285. data/lib/arel/nodes/join_source.rb +20 -0
  286. data/lib/arel/nodes/matches.rb +18 -0
  287. data/lib/arel/nodes/named_function.rb +23 -0
  288. data/lib/arel/nodes/node.rb +50 -0
  289. data/lib/arel/nodes/node_expression.rb +13 -0
  290. data/lib/arel/nodes/outer_join.rb +8 -0
  291. data/lib/arel/nodes/over.rb +15 -0
  292. data/lib/arel/nodes/regexp.rb +16 -0
  293. data/lib/arel/nodes/right_outer_join.rb +8 -0
  294. data/lib/arel/nodes/select_core.rb +67 -0
  295. data/lib/arel/nodes/select_statement.rb +41 -0
  296. data/lib/arel/nodes/sql_literal.rb +16 -0
  297. data/lib/arel/nodes/string_join.rb +11 -0
  298. data/lib/arel/nodes/table_alias.rb +27 -0
  299. data/lib/arel/nodes/terminal.rb +16 -0
  300. data/lib/arel/nodes/true.rb +16 -0
  301. data/lib/arel/nodes/unary.rb +45 -0
  302. data/lib/arel/nodes/unary_operation.rb +20 -0
  303. data/lib/arel/nodes/unqualified_column.rb +22 -0
  304. data/lib/arel/nodes/update_statement.rb +41 -0
  305. data/lib/arel/nodes/values_list.rb +9 -0
  306. data/lib/arel/nodes/window.rb +126 -0
  307. data/lib/arel/nodes/with.rb +11 -0
  308. data/lib/arel/nodes.rb +68 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +256 -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/depth_first.rb +203 -0
  316. data/lib/arel/visitors/dot.rb +296 -0
  317. data/lib/arel/visitors/ibm_db.rb +34 -0
  318. data/lib/arel/visitors/informix.rb +62 -0
  319. data/lib/arel/visitors/mssql.rb +156 -0
  320. data/lib/arel/visitors/mysql.rb +83 -0
  321. data/lib/arel/visitors/oracle.rb +158 -0
  322. data/lib/arel/visitors/oracle12.rb +65 -0
  323. data/lib/arel/visitors/postgresql.rb +109 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +888 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/visitors/where_sql.rb +22 -0
  328. data/lib/arel/visitors.rb +20 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/arel.rb +62 -0
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -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/migration_generator.rb +42 -37
  334. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  335. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -8
  336. data/lib/rails/generators/active_record/migration.rb +30 -1
  337. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record.rb +7 -5
  340. metadata +174 -63
  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 -149
  349. data/lib/active_record/attribute_set/builder.rb +0 -86
  350. data/lib/active_record/attribute_set.rb +0 -77
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  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/integer.rb +0 -11
  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 -30
  362. data/lib/active_record/type/decimal.rb +0 -40
  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 -55
  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 -36
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -101
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
  373. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,4 +1,9 @@
1
- require 'active_record/migration/join_table'
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"
2
7
 
3
8
  module ActiveRecord
4
9
  module ConnectionAdapters # :nodoc:
@@ -12,9 +17,41 @@ module ActiveRecord
12
17
  {}
13
18
  end
14
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
+
15
29
  # Truncates a table alias according to the limits of the current adapter.
16
30
  def table_alias_for(table_name)
17
- table_name[0...table_alias_length].tr('.', '_')
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")
18
55
  end
19
56
 
20
57
  # Checks to see if the table +table_name+ exists on the database.
@@ -22,11 +59,30 @@ module ActiveRecord
22
59
  # table_exists?(:developers)
23
60
  #
24
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
25
64
  tables.include?(table_name.to_s)
26
65
  end
27
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
+
28
82
  # Returns an array of indexes for the given table.
29
- # def indexes(table_name, name = nil) end
83
+ def indexes(table_name)
84
+ raise NotImplementedError, "#indexes is not implemented"
85
+ end
30
86
 
31
87
  # Checks to see if an index exists on a table for a given index definition.
32
88
  #
@@ -44,18 +100,21 @@ module ActiveRecord
44
100
  #
45
101
  def index_exists?(table_name, column_name, options = {})
46
102
  column_names = Array(column_name).map(&:to_s)
47
- index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
48
103
  checks = []
49
- checks << lambda { |i| i.name == index_name }
50
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
51
105
  checks << lambda { |i| i.unique } if options[:unique]
106
+ checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
52
107
 
53
108
  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
54
109
  end
55
110
 
56
- # Returns an array of Column objects for the table specified by +table_name+.
57
- # See the concrete implementation for details on the expected parameter values.
58
- 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
59
118
 
60
119
  # Checks to see if a column exists in a given table.
61
120
  #
@@ -71,21 +130,29 @@ module ActiveRecord
71
130
  # column_exists?(:suppliers, :name, :string, null: false)
72
131
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
73
132
  #
74
- def column_exists?(table_name, column_name, type = nil, options = {})
133
+ def column_exists?(table_name, column_name, type = nil, **options)
75
134
  column_name = column_name.to_s
76
- columns(table_name).any?{ |c| c.name == column_name &&
77
- (!type || c.type == type) &&
78
- (!options.key?(:limit) || c.limit == options[:limit]) &&
79
- (!options.key?(:precision) || c.precision == options[:precision]) &&
80
- (!options.key?(:scale) || c.scale == options[:scale]) &&
81
- (!options.key?(:default) || c.default == options[:default]) &&
82
- (!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
83
150
  end
84
151
 
85
152
  # Creates a new table with the name +table_name+. +table_name+ may either
86
153
  # be a String or a Symbol.
87
154
  #
88
- # 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
89
156
  # form or the regular form, like this:
90
157
  #
91
158
  # === Block form
@@ -117,13 +184,18 @@ module ActiveRecord
117
184
  # The +options+ hash can include the following keys:
118
185
  # [<tt>:id</tt>]
119
186
  # Whether to automatically add a primary key column. Defaults to true.
120
- # 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.
121
190
  # [<tt>:primary_key</tt>]
122
191
  # The name of the primary key, if one is to be added automatically.
123
- # 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.
124
195
  #
125
196
  # Note that Active Record models will automatically detect their
126
- # 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
127
199
  # to define the key explicitly.
128
200
  #
129
201
  # [<tt>:options</tt>]
@@ -134,19 +206,22 @@ module ActiveRecord
134
206
  # Set to true to drop the table before creating it.
135
207
  # Set to +:cascade+ to drop dependent objects as well.
136
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.
137
212
  # [<tt>:as</tt>]
138
213
  # SQL to use to generate the table. When this option is used, the block is
139
214
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
140
215
  #
141
216
  # ====== Add a backend specific option to the generated SQL (MySQL)
142
217
  #
143
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
144
219
  #
145
220
  # generates:
146
221
  #
147
222
  # CREATE TABLE suppliers (
148
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
149
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
223
+ # id bigint auto_increment PRIMARY KEY
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
150
225
  #
151
226
  # ====== Rename the primary key column
152
227
  #
@@ -157,22 +232,52 @@ module ActiveRecord
157
232
  # generates:
158
233
  #
159
234
  # CREATE TABLE objects (
160
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
235
+ # guid bigint auto_increment PRIMARY KEY,
161
236
  # name varchar(80)
162
237
  # )
163
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
+ #
164
269
  # ====== Do not add a primary key column
165
270
  #
166
271
  # create_table(:categories_suppliers, id: false) do |t|
167
- # t.column :category_id, :integer
168
- # t.column :supplier_id, :integer
272
+ # t.column :category_id, :bigint
273
+ # t.column :supplier_id, :bigint
169
274
  # end
170
275
  #
171
276
  # generates:
172
277
  #
173
278
  # CREATE TABLE categories_suppliers (
174
- # category_id int,
175
- # supplier_id int
279
+ # category_id bigint,
280
+ # supplier_id bigint
176
281
  # )
177
282
  #
178
283
  # ====== Create a temporary table based on a query
@@ -186,25 +291,45 @@ module ActiveRecord
186
291
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
187
292
  #
188
293
  # See also TableDefinition#column for details on how to create columns.
189
- def create_table(table_name, options = {})
190
- 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)
191
296
 
192
297
  if options[:id] != false && !options[:as]
193
298
  pk = options.fetch(:primary_key) do
194
299
  Base.get_primary_key table_name.to_s.singularize
195
300
  end
196
301
 
197
- 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
198
307
  end
199
308
 
200
309
  yield td if block_given?
201
310
 
202
- if options[:force] && table_exists?(table_name)
203
- drop_table(table_name, options)
311
+ if options[:force]
312
+ drop_table(table_name, **options, if_exists: true)
204
313
  end
205
314
 
206
315
  result = execute schema_creation.accept td
207
- td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless supports_indexes_in_create?
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
+
208
333
  result
209
334
  end
210
335
 
@@ -214,9 +339,9 @@ module ActiveRecord
214
339
  # # Creates a table called 'assemblies_parts' with no id.
215
340
  # create_join_table(:assemblies, :parts)
216
341
  #
217
- # You can pass a +options+ hash can include the following keys:
342
+ # You can pass an +options+ hash which can include the following keys:
218
343
  # [<tt>:table_name</tt>]
219
- # Sets the table name overriding the default
344
+ # Sets the table name, overriding the default.
220
345
  # [<tt>:column_options</tt>]
221
346
  # Any extra options you want appended to the columns definition.
222
347
  # [<tt>:options</tt>]
@@ -227,7 +352,7 @@ module ActiveRecord
227
352
  # Set to true to drop the table before creating it.
228
353
  # Defaults to false.
229
354
  #
230
- # 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
231
356
  # its block form to do so yourself:
232
357
  #
233
358
  # create_join_table :products, :categories do |t|
@@ -242,32 +367,31 @@ module ActiveRecord
242
367
  # generates:
243
368
  #
244
369
  # CREATE TABLE assemblies_parts (
245
- # assembly_id int NOT NULL,
246
- # part_id int NOT NULL,
370
+ # assembly_id bigint NOT NULL,
371
+ # part_id bigint NOT NULL,
247
372
  # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
248
373
  #
249
- def create_join_table(table_1, table_2, options = {})
374
+ def create_join_table(table_1, table_2, column_options: {}, **options)
250
375
  join_table_name = find_join_table_name(table_1, table_2, options)
251
376
 
252
- column_options = options.delete(:column_options) || {}
253
- column_options.reverse_merge!(null: false)
377
+ column_options.reverse_merge!(null: false, index: false)
254
378
 
255
- 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 }
256
380
 
257
- create_table(join_table_name, options.merge!(id: false)) do |td|
258
- td.integer t1_column, column_options
259
- td.integer t2_column, column_options
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
260
384
  yield td if block_given?
261
385
  end
262
386
  end
263
387
 
264
388
  # Drops the join table specified by the given arguments.
265
- # See +create_join_table+ for details.
389
+ # See #create_join_table for details.
266
390
  #
267
391
  # Although this command ignores the block if one is given, it can be helpful
268
392
  # to provide one in a migration's +change+ method so it can be reverted.
269
- # In that case, the block will be used by create_join_table.
270
- def drop_join_table(table_1, table_2, options = {})
393
+ # In that case, the block will be used by #create_join_table.
394
+ def drop_join_table(table_1, table_2, **options)
271
395
  join_table_name = find_join_table_name(table_1, table_2, options)
272
396
  drop_table(join_table_name)
273
397
  end
@@ -284,10 +408,12 @@ module ActiveRecord
284
408
  # [<tt>:bulk</tt>]
285
409
  # Set this to true to make this a bulk alter query, such as
286
410
  #
287
- # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
411
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
288
412
  #
289
413
  # Defaults to false.
290
414
  #
415
+ # Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
416
+ #
291
417
  # ====== Add a column
292
418
  #
293
419
  # change_table(:suppliers) do |t|
@@ -312,7 +438,7 @@ module ActiveRecord
312
438
  # t.references :company
313
439
  # end
314
440
  #
315
- # Creates a <tt>company_id(integer)</tt> column.
441
+ # Creates a <tt>company_id(bigint)</tt> column.
316
442
  #
317
443
  # ====== Add a polymorphic foreign key column
318
444
  #
@@ -320,7 +446,7 @@ module ActiveRecord
320
446
  # t.belongs_to :company, polymorphic: true
321
447
  # end
322
448
  #
323
- # 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.
324
450
  #
325
451
  # ====== Remove a column
326
452
  #
@@ -341,8 +467,8 @@ module ActiveRecord
341
467
  # t.remove_index :company_id
342
468
  # end
343
469
  #
344
- # See also Table for details on all of the various column transformation.
345
- def change_table(table_name, options = {})
470
+ # See also Table for details on all of the various column transformations.
471
+ def change_table(table_name, **options)
346
472
  if supports_bulk_alter? && options[:bulk]
347
473
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
348
474
  yield update_table_definition(table_name, recorder)
@@ -365,19 +491,103 @@ module ActiveRecord
365
491
  # [<tt>:force</tt>]
366
492
  # Set to +:cascade+ to drop dependent objects as well.
367
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.
368
497
  #
369
498
  # Although this command ignores most +options+ and the block if one is given,
370
499
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
371
- # In that case, +options+ and the block will be used by create_table.
372
- def drop_table(table_name, options = {})
373
- execute "DROP TABLE #{quote_table_name(table_name)}"
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)}"
374
503
  end
375
504
 
376
- # Adds a new column to the named table.
377
- # See TableDefinition#column for details of the options you can use.
378
- 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)
379
589
  at = create_alter_table table_name
380
- at.add_column(column_name, type, options)
590
+ at.add_column(column_name, type, **options)
381
591
  execute schema_creation.accept at
382
592
  end
383
593
 
@@ -398,9 +608,10 @@ module ActiveRecord
398
608
  #
399
609
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
400
610
  # to provide these in a migration's +change+ method so it can be reverted.
401
- # In that case, +type+ and +options+ will be used by add_column.
402
- def remove_column(table_name, column_name, type = nil, options = {})
403
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
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)}"
404
615
  end
405
616
 
406
617
  # Changes the column's definition according to the new options.
@@ -422,11 +633,16 @@ module ActiveRecord
422
633
  #
423
634
  # change_column_default(:users, :email, nil)
424
635
  #
425
- 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)
426
642
  raise NotImplementedError, "change_column_default is not implemented"
427
643
  end
428
644
 
429
- # 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
430
646
  # indicates whether the value can be +NULL+. For example
431
647
  #
432
648
  # change_column_null(:users, :nickname, false)
@@ -438,7 +654,7 @@ module ActiveRecord
438
654
  # allows them to be +NULL+ (drops the constraint).
439
655
  #
440
656
  # The method accepts an optional fourth argument to replace existing
441
- # +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
442
658
  # constraint if needed, since otherwise those rows would not be valid.
443
659
  #
444
660
  # Please note the fourth argument does not set a column's default.
@@ -492,6 +708,8 @@ module ActiveRecord
492
708
  #
493
709
  # CREATE INDEX by_name ON accounts(name(10))
494
710
  #
711
+ # ====== Creating an index with specific key lengths for multiple keys
712
+ #
495
713
  # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
496
714
  #
497
715
  # generates:
@@ -508,7 +726,7 @@ module ActiveRecord
508
726
  #
509
727
  # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
510
728
  #
511
- # 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).
512
730
  #
513
731
  # ====== Creating a partial index
514
732
  #
@@ -518,6 +736,8 @@ module ActiveRecord
518
736
  #
519
737
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
520
738
  #
739
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
740
+ #
521
741
  # ====== Creating an index with a specific method
522
742
  #
523
743
  # add_index(:developers, :name, using: 'btree')
@@ -529,6 +749,19 @@ module ActiveRecord
529
749
  #
530
750
  # Note: only supported by PostgreSQL and MySQL
531
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
+ #
532
765
  # ====== Creating an index with a specific type
533
766
  #
534
767
  # add_index(:developers, :name, type: :fulltext)
@@ -537,23 +770,34 @@ module ActiveRecord
537
770
  #
538
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
539
772
  #
540
- # 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].
541
785
  def add_index(table_name, column_name, options = {})
542
- index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
786
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, **options)
543
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
544
788
  end
545
789
 
546
790
  # Removes the given index from the table.
547
791
  #
548
- # 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.
549
793
  #
550
- # remove_index :accounts, :column
794
+ # remove_index :accounts, :branch_id
551
795
  #
552
- # 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.
553
797
  #
554
798
  # remove_index :accounts, column: :branch_id
555
799
  #
556
- # 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.
557
801
  #
558
802
  # remove_index :accounts, column: [:branch_id, :party_id]
559
803
  #
@@ -561,11 +805,17 @@ module ActiveRecord
561
805
  #
562
806
  # remove_index :accounts, name: :by_branch_party
563
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].
564
817
  def remove_index(table_name, options = {})
565
- remove_index!(table_name, index_name_for_remove(table_name, options))
566
- end
567
-
568
- def remove_index!(table_name, index_name) #:nodoc:
818
+ index_name = index_name_for_remove(table_name, options)
569
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
570
820
  end
571
821
 
@@ -576,10 +826,9 @@ module ActiveRecord
576
826
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
577
827
  #
578
828
  def rename_index(table_name, old_name, new_name)
579
- if new_name.length > allowed_index_name_length
580
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
581
- end
582
- # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
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)
583
832
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
584
833
  return unless old_index_def
585
834
  add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
@@ -596,70 +845,101 @@ module ActiveRecord
596
845
  raise ArgumentError, "You must specify the index name"
597
846
  end
598
847
  else
599
- index_name(table_name, :column => options)
848
+ index_name(table_name, index_name_options(options))
600
849
  end
601
850
  end
602
851
 
603
852
  # Verifies the existence of an index with a given name.
604
- #
605
- # The default argument is returned if the underlying implementation does not define the indexes method,
606
- # as there's no way to determine the correct answer in that case.
607
- def index_name_exists?(table_name, index_name, default)
608
- return default unless respond_to?(:indexes)
853
+ def index_name_exists?(table_name, index_name)
609
854
  index_name = index_name.to_s
610
855
  indexes(table_name).detect { |i| i.name == index_name }
611
856
  end
612
857
 
613
- # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
614
- # The reference column is an +integer+ by default, the <tt>:type</tt> option can be used to specify
615
- # a different type.
616
- # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
617
- #
618
- # ====== Create a user_id integer column
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.
619
862
  #
620
- # add_reference(:products, :user)
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)
621
879
  #
622
880
  # ====== Create a user_id string column
623
881
  #
624
882
  # add_reference(:products, :user, type: :string)
625
883
  #
626
- # ====== Create a supplier_id and supplier_type columns
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
627
897
  #
628
- # add_belongs_to(:products, :supplier, polymorphic: true)
898
+ # add_reference(:products, :supplier, foreign_key: true)
629
899
  #
630
- # ====== Create a supplier_id, supplier_type columns and appropriate index
900
+ # ====== Create a supplier_id column and a foreign key to the firms table
631
901
  #
632
- # add_reference(:products, :supplier, polymorphic: true, index: true)
902
+ # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
633
903
  #
634
- def add_reference(table_name, ref_name, options = {})
635
- polymorphic = options.delete(:polymorphic)
636
- index_options = options.delete(:index)
637
- type = options.delete(:type) || :integer
638
- add_column(table_name, "#{ref_name}_id", type, options)
639
- add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
640
- 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
904
+ def add_reference(table_name, ref_name, **options)
905
+ ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
641
906
  end
642
907
  alias :add_belongs_to :add_reference
643
908
 
644
909
  # Removes the reference(s). Also removes a +type+ column if one exists.
645
- # <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.
646
911
  #
647
912
  # ====== Remove the reference
648
913
  #
649
- # remove_reference(:products, :user, index: true)
914
+ # remove_reference(:products, :user, index: false)
650
915
  #
651
916
  # ====== Remove polymorphic reference
652
917
  #
653
918
  # remove_reference(:products, :supplier, polymorphic: true)
654
919
  #
655
- def remove_reference(table_name, ref_name, options = {})
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
+
656
936
  remove_column(table_name, "#{ref_name}_id")
657
- remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
937
+ remove_column(table_name, "#{ref_name}_type") if polymorphic
658
938
  end
659
939
  alias :remove_belongs_to :remove_reference
660
940
 
661
941
  # Returns an array of foreign keys for the given table.
662
- # The foreign keys are represented as +ForeignKeyDefinition+ objects.
942
+ # The foreign keys are represented as ForeignKeyDefinition objects.
663
943
  def foreign_keys(table_name)
664
944
  raise NotImplementedError, "foreign_keys is not implemented"
665
945
  end
@@ -668,8 +948,8 @@ module ActiveRecord
668
948
  # +to_table+ contains the referenced primary key.
669
949
  #
670
950
  # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
671
- # +identifier+ is a 10 character long random string. A custom name can be specified with
672
- # the <tt>:name</tt> option.
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.
673
953
  #
674
954
  # ====== Creating a simple foreign key
675
955
  #
@@ -677,7 +957,7 @@ module ActiveRecord
677
957
  #
678
958
  # generates:
679
959
  #
680
- # 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")
681
961
  #
682
962
  # ====== Creating a foreign key on a specific column
683
963
  #
@@ -693,7 +973,7 @@ module ActiveRecord
693
973
  #
694
974
  # generates:
695
975
  #
696
- # 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
697
977
  #
698
978
  # The +options+ hash can include the following keys:
699
979
  # [<tt>:column</tt>]
@@ -703,28 +983,25 @@ module ActiveRecord
703
983
  # [<tt>:name</tt>]
704
984
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
705
985
  # [<tt>:on_delete</tt>]
706
- # 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+
707
987
  # [<tt>:on_update</tt>]
708
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
709
- def add_foreign_key(from_table, to_table, options = {})
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)
710
992
  return unless supports_foreign_keys?
711
993
 
712
- options[:column] ||= foreign_key_column_for(to_table)
713
-
714
- options = {
715
- column: options[:column],
716
- primary_key: options[:primary_key],
717
- name: foreign_key_name(from_table, options),
718
- on_delete: options[:on_delete],
719
- on_update: options[:on_update]
720
- }
994
+ options = foreign_key_options(from_table, to_table, options)
721
995
  at = create_alter_table from_table
722
996
  at.add_foreign_key to_table, options
723
997
 
724
998
  execute schema_creation.accept(at)
725
999
  end
726
1000
 
727
- # 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.
728
1005
  #
729
1006
  # Removes the foreign key on +accounts.branch_id+.
730
1007
  #
@@ -734,28 +1011,22 @@ module ActiveRecord
734
1011
  #
735
1012
  # remove_foreign_key :accounts, column: :owner_id
736
1013
  #
1014
+ # Removes the foreign key on +accounts.owner_id+.
1015
+ #
1016
+ # remove_foreign_key :accounts, to_table: :owners
1017
+ #
737
1018
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
738
1019
  #
739
1020
  # remove_foreign_key :accounts, name: :special_fk_name
740
1021
  #
741
- 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)
742
1027
  return unless supports_foreign_keys?
743
1028
 
744
- if options_or_to_table.is_a?(Hash)
745
- options = options_or_to_table
746
- else
747
- options = { column: foreign_key_column_for(options_or_to_table) }
748
- end
749
-
750
- fk_name_to_delete = options.fetch(:name) do
751
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
752
-
753
- if fk_to_delete
754
- fk_to_delete.name
755
- else
756
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
757
- end
758
- end
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
759
1030
 
760
1031
  at = create_alter_table from_table
761
1032
  at.drop_foreign_key fk_name_to_delete
@@ -763,52 +1034,71 @@ module ActiveRecord
763
1034
  execute schema_creation.accept(at)
764
1035
  end
765
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
+
766
1052
  def foreign_key_column_for(table_name) # :nodoc:
767
- "#{table_name.to_s.singularize}_id"
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
1054
+ "#{name.singularize}_id"
768
1055
  end
769
1056
 
770
- def dump_schema_information #:nodoc:
771
- 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
772
1063
 
773
- ActiveRecord::SchemaMigration.order('version').map { |sm|
774
- "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
775
- }.join "\n\n"
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1066
+ insert_versions_sql(versions) if versions.any?
776
1067
  end
777
1068
 
778
- # Should not be called normally, but this operation is non-destructive.
779
- # The migrations module handles this automatically.
780
- def initialize_schema_migrations_table
781
- ActiveRecord::SchemaMigration.create_table
1069
+ def internal_string_options_for_primary_key # :nodoc:
1070
+ { primary_key: true }
782
1071
  end
783
1072
 
784
- def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
785
- 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
+
786
1080
  version = version.to_i
787
- sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
788
1082
 
789
- migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
790
- paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
791
- versions = Dir[*paths].map do |filename|
792
- filename.split('/').last.split('_').first.to_i
793
- end
1083
+ migrated = migration_context.get_all_versions
1084
+ versions = migration_context.migrations.map(&:version)
794
1085
 
795
1086
  unless migrated.include?(version)
796
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
1087
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
797
1088
  end
798
1089
 
799
- inserted = Set.new
800
- (versions - migrated).each do |v|
801
- if inserted.include?(v)
802
- raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
803
- elsif v < version
804
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
805
- 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."
806
1094
  end
1095
+ execute insert_versions_sql(inserting)
807
1096
  end
808
1097
  end
809
1098
 
810
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
811
- 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]
812
1102
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
813
1103
 
814
1104
  if type == :decimal # ignore limit, use precision and scale
@@ -824,6 +1114,12 @@ module ActiveRecord
824
1114
  raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
825
1115
  end
826
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
827
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
828
1124
  column_type_sql << "(#{limit})"
829
1125
  end
@@ -835,31 +1131,36 @@ module ActiveRecord
835
1131
  end
836
1132
 
837
1133
  # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
838
- # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
1134
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
839
1135
  # require the order columns appear in the SELECT.
840
1136
  #
841
1137
  # columns_for_distinct("posts.id", ["posts.created_at desc"])
842
- def columns_for_distinct(columns, orders) #:nodoc:
1138
+ #
1139
+ def columns_for_distinct(columns, orders) # :nodoc:
843
1140
  columns
844
1141
  end
845
1142
 
846
- include TimestampDefaultDeprecation
847
1143
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
848
- # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
1144
+ # Additional options (like +:null+) are forwarded to #add_column.
849
1145
  #
850
- # add_timestamps(:suppliers, null: false)
1146
+ # add_timestamps(:suppliers, null: true)
851
1147
  #
852
- def add_timestamps(table_name, options = {})
853
- emit_warning_if_null_unspecified(options)
854
- add_column table_name, :created_at, :datetime, options
855
- add_column table_name, :updated_at, :datetime, options
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
856
1157
  end
857
1158
 
858
1159
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
859
1160
  #
860
1161
  # remove_timestamps(:suppliers)
861
1162
  #
862
- def remove_timestamps(table_name, options = {})
1163
+ def remove_timestamps(table_name, **options)
863
1164
  remove_column table_name, :updated_at
864
1165
  remove_column table_name, :created_at
865
1166
  end
@@ -868,16 +1169,15 @@ module ActiveRecord
868
1169
  Table.new(table_name, base)
869
1170
  end
870
1171
 
871
- def add_index_options(table_name, column_name, options = {}) #:nodoc:
872
- column_names = Array(column_name)
873
- 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)
874
1174
 
875
- 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)
876
1176
 
877
- index_type = options[:unique] ? "UNIQUE" : ""
878
1177
  index_type = options[:type].to_s if options.key?(:type)
1178
+ index_type ||= options[:unique] ? "UNIQUE" : ""
879
1179
  index_name = options[:name].to_s if options.key?(:name)
880
- max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
1180
+ index_name ||= index_name(table_name, column_names)
881
1181
 
882
1182
  if options.key?(:algorithm)
883
1183
  algorithm = index_algorithms.fetch(options[:algorithm]) {
@@ -891,63 +1191,109 @@ module ActiveRecord
891
1191
  index_options = options[:where] ? " WHERE #{options[:where]}" : ""
892
1192
  end
893
1193
 
894
- if index_name.length > max_index_length
895
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
896
- end
897
- 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)
898
1197
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
899
1198
  end
900
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
1199
+ index_columns = quoted_columns_for_index(column_names, **options).join(", ")
901
1200
 
902
- [index_name, index_type, index_columns, index_options, algorithm, using]
1201
+ [index_name, index_type, index_columns, index_options, algorithm, using, comment]
903
1202
  end
904
1203
 
905
- protected
906
- def add_index_sort_order(option_strings, column_names, options = {})
907
- if options.is_a?(Hash) && order = options[:order]
908
- case order
909
- when Hash
910
- column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
911
- when String
912
- column_names.each {|name| option_strings[name] += " #{order.upcase}"}
913
- end
914
- 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
915
1217
 
916
- return option_strings
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]
917
1235
  end
918
1236
 
919
- # Overridden by the MySQL adapter for supporting index lengths
920
- def quoted_columns_for_index(column_names, options = {})
921
- option_strings = Hash[column_names.map {|name| [name, '']}]
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
922
1251
 
923
- # 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)
924
1255
  if supports_index_sort_order?
925
- option_strings = add_index_sort_order(option_strings, column_names, options)
1256
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
926
1257
  end
927
1258
 
928
- column_names.map {|name| quote_column_name(name) + option_strings[name]}
1259
+ quoted_columns
929
1260
  end
930
1261
 
931
- def options_include_default?(options)
932
- 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
933
1267
  end
934
1268
 
935
1269
  def index_name_for_remove(table_name, options = {})
936
- index_name = index_name(table_name, options)
1270
+ return options[:name] if can_remove_index_by_name?(options)
937
1271
 
938
- unless index_name_exists?(table_name, index_name, true)
939
- if options.is_a?(Hash) && options.has_key?(:name)
940
- options_without_column = options.dup
941
- options_without_column.delete :column
942
- index_name_without_column = index_name(table_name, options_without_column)
1272
+ checks = []
943
1273
 
944
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
945
- 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
946
1280
 
947
- 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) }
948
1283
  end
949
1284
 
950
- 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
951
1297
  end
952
1298
 
953
1299
  def rename_table_indexes(table_name, new_name)
@@ -972,20 +1318,175 @@ module ActiveRecord
972
1318
  end
973
1319
  end
974
1320
 
975
- private
976
- def create_table_definition(name, temporary, options, as = nil)
977
- TableDefinition.new native_database_types, name, temporary, options, as
978
- end
1321
+ def schema_creation
1322
+ SchemaCreation.new(self)
1323
+ end
979
1324
 
980
- def create_alter_table(name)
981
- AlterTable.new create_table_definition(name, false, {})
982
- end
1325
+ def create_table_definition(*args, **options)
1326
+ TableDefinition.new(self, *args, **options)
1327
+ end
983
1328
 
984
- def foreign_key_name(table_name, options) # :nodoc:
985
- options.fetch(:name) do
986
- "fk_rails_#{SecureRandom.hex(5)}"
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, **options)
1450
+ column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1451
+ end
1452
+
1453
+ def add_timestamps_for_alter(table_name, **options)
1454
+ options[:null] = false if options[:null].nil?
1455
+
1456
+ if !options.key?(:precision) && supports_datetime_with_precision?
1457
+ options[:precision] = 6
1458
+ end
1459
+
1460
+ [
1461
+ add_column_for_alter(table_name, :created_at, :datetime, **options),
1462
+ add_column_for_alter(table_name, :updated_at, :datetime, **options)
1463
+ ]
1464
+ end
1465
+
1466
+ def remove_timestamps_for_alter(table_name, **options)
1467
+ remove_columns_for_alter(table_name, :updated_at, :created_at)
1468
+ end
1469
+
1470
+ def insert_versions_sql(versions)
1471
+ sm_table = quote_table_name(schema_migration.table_name)
1472
+
1473
+ if versions.is_a?(Array)
1474
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1475
+ sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1476
+ sql << ";\n\n"
1477
+ sql
1478
+ else
1479
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1480
+ end
1481
+ end
1482
+
1483
+ def data_source_sql(name = nil, type: nil)
1484
+ raise NotImplementedError
1485
+ end
1486
+
1487
+ def quoted_scope(name = nil, type: nil)
1488
+ raise NotImplementedError
987
1489
  end
988
- end
989
1490
  end
990
1491
  end
991
1492
  end