activerecord 3.2.6 → 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 (371) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +611 -6417
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +44 -47
  5. data/examples/performance.rb +79 -71
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +268 -238
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +173 -81
  11. data/lib/active_record/associations/association_scope.rb +124 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +83 -38
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
  14. data/lib/active_record/associations/builder/association.rb +113 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +105 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +53 -56
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
  18. data/lib/active_record/associations/builder/has_many.rb +11 -63
  19. data/lib/active_record/associations/builder/has_one.rb +47 -45
  20. data/lib/active_record/associations/builder/singular_association.rb +30 -18
  21. data/lib/active_record/associations/collection_association.rb +217 -295
  22. data/lib/active_record/associations/collection_proxy.rb +1074 -77
  23. data/lib/active_record/associations/foreign_association.rb +20 -0
  24. data/lib/active_record/associations/has_many_association.rb +78 -50
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -61
  26. data/lib/active_record/associations/has_one_association.rb +75 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  28. data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +208 -164
  32. data/lib/active_record/associations/preloader/association.rb +93 -87
  33. data/lib/active_record/associations/preloader/through_association.rb +87 -38
  34. data/lib/active_record/associations/preloader.rb +134 -110
  35. data/lib/active_record/associations/singular_association.rb +19 -24
  36. data/lib/active_record/associations/through_association.rb +61 -27
  37. data/lib/active_record/associations.rb +1766 -1505
  38. data/lib/active_record/attribute_assignment.rb +57 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +187 -67
  42. data/lib/active_record/attribute_methods/primary_key.rb +100 -78
  43. data/lib/active_record/attribute_methods/query.rb +10 -8
  44. data/lib/active_record/attribute_methods/read.rb +29 -118
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -72
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
  47. data/lib/active_record/attribute_methods/write.rb +36 -44
  48. data/lib/active_record/attribute_methods.rb +306 -161
  49. data/lib/active_record/attributes.rb +279 -0
  50. data/lib/active_record/autosave_association.rb +324 -238
  51. data/lib/active_record/base.rb +114 -507
  52. data/lib/active_record/callbacks.rb +147 -83
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
  56. data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
  57. data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
  58. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
  59. data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
  60. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
  63. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
  64. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
  65. data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
  66. data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
  67. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
  68. data/lib/active_record/connection_adapters/column.rb +58 -233
  69. data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
  70. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +200 -0
  73. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  74. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  79. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +75 -207
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
  83. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
  116. data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  118. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +528 -26
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +267 -0
  128. data/lib/active_record/core.rb +599 -0
  129. data/lib/active_record/counter_cache.rb +177 -103
  130. data/lib/active_record/database_configurations/database_config.rb +37 -0
  131. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  132. data/lib/active_record/database_configurations/url_config.rb +79 -0
  133. data/lib/active_record/database_configurations.rb +233 -0
  134. data/lib/active_record/define_callbacks.rb +22 -0
  135. data/lib/active_record/dynamic_matchers.rb +107 -64
  136. data/lib/active_record/enum.rb +274 -0
  137. data/lib/active_record/errors.rb +254 -61
  138. data/lib/active_record/explain.rb +35 -70
  139. data/lib/active_record/explain_registry.rb +32 -0
  140. data/lib/active_record/explain_subscriber.rb +18 -8
  141. data/lib/active_record/fixture_set/file.rb +82 -0
  142. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  143. data/lib/active_record/fixture_set/render_context.rb +17 -0
  144. data/lib/active_record/fixture_set/table_row.rb +153 -0
  145. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  146. data/lib/active_record/fixtures.rb +291 -475
  147. data/lib/active_record/gem_version.rb +17 -0
  148. data/lib/active_record/inheritance.rb +219 -100
  149. data/lib/active_record/insert_all.rb +179 -0
  150. data/lib/active_record/integration.rb +175 -17
  151. data/lib/active_record/internal_metadata.rb +53 -0
  152. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  153. data/lib/active_record/locale/en.yml +9 -1
  154. data/lib/active_record/locking/optimistic.rb +106 -92
  155. data/lib/active_record/locking/pessimistic.rb +23 -11
  156. data/lib/active_record/log_subscriber.rb +80 -30
  157. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  158. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  159. data/lib/active_record/middleware/database_selector.rb +75 -0
  160. data/lib/active_record/migration/command_recorder.rb +235 -56
  161. data/lib/active_record/migration/compatibility.rb +244 -0
  162. data/lib/active_record/migration/join_table.rb +17 -0
  163. data/lib/active_record/migration.rb +917 -301
  164. data/lib/active_record/model_schema.rb +351 -175
  165. data/lib/active_record/nested_attributes.rb +366 -235
  166. data/lib/active_record/no_touching.rb +65 -0
  167. data/lib/active_record/null_relation.rb +68 -0
  168. data/lib/active_record/persistence.rb +761 -166
  169. data/lib/active_record/query_cache.rb +22 -44
  170. data/lib/active_record/querying.rb +55 -31
  171. data/lib/active_record/railtie.rb +185 -47
  172. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  173. data/lib/active_record/railties/console_sandbox.rb +5 -4
  174. data/lib/active_record/railties/controller_runtime.rb +35 -33
  175. data/lib/active_record/railties/databases.rake +366 -463
  176. data/lib/active_record/readonly_attributes.rb +4 -6
  177. data/lib/active_record/reflection.rb +736 -228
  178. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  179. data/lib/active_record/relation/batches.rb +252 -52
  180. data/lib/active_record/relation/calculations.rb +340 -270
  181. data/lib/active_record/relation/delegation.rb +117 -36
  182. data/lib/active_record/relation/finder_methods.rb +439 -286
  183. data/lib/active_record/relation/from_clause.rb +26 -0
  184. data/lib/active_record/relation/merger.rb +184 -0
  185. data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
  186. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  187. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  188. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  189. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  190. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  191. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder.rb +131 -39
  193. data/lib/active_record/relation/query_attribute.rb +50 -0
  194. data/lib/active_record/relation/query_methods.rb +1163 -221
  195. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  196. data/lib/active_record/relation/spawn_methods.rb +49 -120
  197. data/lib/active_record/relation/where_clause.rb +190 -0
  198. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  199. data/lib/active_record/relation.rb +671 -349
  200. data/lib/active_record/result.rb +149 -15
  201. data/lib/active_record/runtime_registry.rb +24 -0
  202. data/lib/active_record/sanitization.rb +153 -133
  203. data/lib/active_record/schema.rb +22 -19
  204. data/lib/active_record/schema_dumper.rb +178 -112
  205. data/lib/active_record/schema_migration.rb +60 -0
  206. data/lib/active_record/scoping/default.rb +107 -98
  207. data/lib/active_record/scoping/named.rb +130 -115
  208. data/lib/active_record/scoping.rb +77 -123
  209. data/lib/active_record/secure_token.rb +40 -0
  210. data/lib/active_record/serialization.rb +10 -6
  211. data/lib/active_record/statement_cache.rb +148 -0
  212. data/lib/active_record/store.rb +256 -16
  213. data/lib/active_record/suppressor.rb +61 -0
  214. data/lib/active_record/table_metadata.rb +75 -0
  215. data/lib/active_record/tasks/database_tasks.rb +506 -0
  216. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  217. data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
  218. data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
  219. data/lib/active_record/test_databases.rb +23 -0
  220. data/lib/active_record/test_fixtures.rb +224 -0
  221. data/lib/active_record/timestamp.rb +93 -39
  222. data/lib/active_record/touch_later.rb +66 -0
  223. data/lib/active_record/transactions.rb +260 -129
  224. data/lib/active_record/translation.rb +3 -1
  225. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  226. data/lib/active_record/type/date.rb +9 -0
  227. data/lib/active_record/type/date_time.rb +9 -0
  228. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  229. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  230. data/lib/active_record/type/internal/timezone.rb +17 -0
  231. data/lib/active_record/type/json.rb +30 -0
  232. data/lib/active_record/type/serialized.rb +71 -0
  233. data/lib/active_record/type/text.rb +11 -0
  234. data/lib/active_record/type/time.rb +21 -0
  235. data/lib/active_record/type/type_map.rb +62 -0
  236. data/lib/active_record/type/unsigned_integer.rb +17 -0
  237. data/lib/active_record/type.rb +78 -0
  238. data/lib/active_record/type_caster/connection.rb +34 -0
  239. data/lib/active_record/type_caster/map.rb +20 -0
  240. data/lib/active_record/type_caster.rb +9 -0
  241. data/lib/active_record/validations/absence.rb +25 -0
  242. data/lib/active_record/validations/associated.rb +35 -18
  243. data/lib/active_record/validations/length.rb +26 -0
  244. data/lib/active_record/validations/presence.rb +68 -0
  245. data/lib/active_record/validations/uniqueness.rb +123 -77
  246. data/lib/active_record/validations.rb +54 -43
  247. data/lib/active_record/version.rb +7 -7
  248. data/lib/active_record.rb +97 -49
  249. data/lib/arel/alias_predication.rb +9 -0
  250. data/lib/arel/attributes/attribute.rb +37 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/collectors/bind.rb +24 -0
  253. data/lib/arel/collectors/composite.rb +31 -0
  254. data/lib/arel/collectors/plain_string.rb +20 -0
  255. data/lib/arel/collectors/sql_string.rb +20 -0
  256. data/lib/arel/collectors/substitute_binds.rb +28 -0
  257. data/lib/arel/crud.rb +42 -0
  258. data/lib/arel/delete_manager.rb +18 -0
  259. data/lib/arel/errors.rb +9 -0
  260. data/lib/arel/expressions.rb +29 -0
  261. data/lib/arel/factory_methods.rb +49 -0
  262. data/lib/arel/insert_manager.rb +49 -0
  263. data/lib/arel/math.rb +45 -0
  264. data/lib/arel/nodes/and.rb +32 -0
  265. data/lib/arel/nodes/ascending.rb +23 -0
  266. data/lib/arel/nodes/binary.rb +52 -0
  267. data/lib/arel/nodes/bind_param.rb +36 -0
  268. data/lib/arel/nodes/case.rb +55 -0
  269. data/lib/arel/nodes/casted.rb +50 -0
  270. data/lib/arel/nodes/comment.rb +29 -0
  271. data/lib/arel/nodes/count.rb +12 -0
  272. data/lib/arel/nodes/delete_statement.rb +45 -0
  273. data/lib/arel/nodes/descending.rb +23 -0
  274. data/lib/arel/nodes/equality.rb +18 -0
  275. data/lib/arel/nodes/extract.rb +24 -0
  276. data/lib/arel/nodes/false.rb +16 -0
  277. data/lib/arel/nodes/full_outer_join.rb +8 -0
  278. data/lib/arel/nodes/function.rb +44 -0
  279. data/lib/arel/nodes/grouping.rb +8 -0
  280. data/lib/arel/nodes/in.rb +8 -0
  281. data/lib/arel/nodes/infix_operation.rb +80 -0
  282. data/lib/arel/nodes/inner_join.rb +8 -0
  283. data/lib/arel/nodes/insert_statement.rb +37 -0
  284. data/lib/arel/nodes/join_source.rb +20 -0
  285. data/lib/arel/nodes/matches.rb +18 -0
  286. data/lib/arel/nodes/named_function.rb +23 -0
  287. data/lib/arel/nodes/node.rb +50 -0
  288. data/lib/arel/nodes/node_expression.rb +13 -0
  289. data/lib/arel/nodes/outer_join.rb +8 -0
  290. data/lib/arel/nodes/over.rb +15 -0
  291. data/lib/arel/nodes/regexp.rb +16 -0
  292. data/lib/arel/nodes/right_outer_join.rb +8 -0
  293. data/lib/arel/nodes/select_core.rb +67 -0
  294. data/lib/arel/nodes/select_statement.rb +41 -0
  295. data/lib/arel/nodes/sql_literal.rb +16 -0
  296. data/lib/arel/nodes/string_join.rb +11 -0
  297. data/lib/arel/nodes/table_alias.rb +27 -0
  298. data/lib/arel/nodes/terminal.rb +16 -0
  299. data/lib/arel/nodes/true.rb +16 -0
  300. data/lib/arel/nodes/unary.rb +45 -0
  301. data/lib/arel/nodes/unary_operation.rb +20 -0
  302. data/lib/arel/nodes/unqualified_column.rb +22 -0
  303. data/lib/arel/nodes/update_statement.rb +41 -0
  304. data/lib/arel/nodes/values_list.rb +9 -0
  305. data/lib/arel/nodes/window.rb +126 -0
  306. data/lib/arel/nodes/with.rb +11 -0
  307. data/lib/arel/nodes.rb +68 -0
  308. data/lib/arel/order_predications.rb +13 -0
  309. data/lib/arel/predications.rb +257 -0
  310. data/lib/arel/select_manager.rb +271 -0
  311. data/lib/arel/table.rb +110 -0
  312. data/lib/arel/tree_manager.rb +72 -0
  313. data/lib/arel/update_manager.rb +34 -0
  314. data/lib/arel/visitors/depth_first.rb +204 -0
  315. data/lib/arel/visitors/dot.rb +297 -0
  316. data/lib/arel/visitors/ibm_db.rb +34 -0
  317. data/lib/arel/visitors/informix.rb +62 -0
  318. data/lib/arel/visitors/mssql.rb +157 -0
  319. data/lib/arel/visitors/mysql.rb +83 -0
  320. data/lib/arel/visitors/oracle.rb +159 -0
  321. data/lib/arel/visitors/oracle12.rb +66 -0
  322. data/lib/arel/visitors/postgresql.rb +110 -0
  323. data/lib/arel/visitors/sqlite.rb +39 -0
  324. data/lib/arel/visitors/to_sql.rb +889 -0
  325. data/lib/arel/visitors/visitor.rb +46 -0
  326. data/lib/arel/visitors/where_sql.rb +23 -0
  327. data/lib/arel/visitors.rb +20 -0
  328. data/lib/arel/window_predications.rb +9 -0
  329. data/lib/arel.rb +51 -0
  330. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  331. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
  333. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  334. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  335. data/lib/rails/generators/active_record/migration.rb +41 -8
  336. data/lib/rails/generators/active_record/model/model_generator.rb +24 -22
  337. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  339. data/lib/rails/generators/active_record.rb +10 -16
  340. metadata +285 -149
  341. data/examples/associations.png +0 -0
  342. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  343. data/lib/active_record/associations/join_helper.rb +0 -55
  344. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  345. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  346. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  347. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  348. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  349. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  350. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  351. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  352. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  353. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
  354. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
  355. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
  356. data/lib/active_record/dynamic_finder_match.rb +0 -68
  357. data/lib/active_record/dynamic_scope_match.rb +0 -23
  358. data/lib/active_record/fixtures/file.rb +0 -65
  359. data/lib/active_record/identity_map.rb +0 -162
  360. data/lib/active_record/observer.rb +0 -121
  361. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  362. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  363. data/lib/active_record/session_store.rb +0 -358
  364. data/lib/active_record/test_case.rb +0 -73
  365. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  366. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  367. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  368. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  369. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  370. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  371. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,34 +1,191 @@
1
- require "active_support/core_ext/module/delegation"
2
- require "active_support/core_ext/class/attribute_accessors"
3
- require "active_support/core_ext/array/wrap"
4
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "set"
5
+ require "zlib"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
5
8
 
6
9
  module ActiveRecord
7
- # Exception that can be raised to stop migrations from going backwards.
8
- class IrreversibleMigration < ActiveRecordError
10
+ class MigrationError < ActiveRecordError #:nodoc:
11
+ def initialize(message = nil)
12
+ message = "\n\n#{message}\n\n" if message
13
+ super
14
+ end
15
+ end
16
+
17
+ # Exception that can be raised to stop migrations from being rolled back.
18
+ # For example the following migration is not reversible.
19
+ # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
20
+ #
21
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[5.0]
22
+ # def change
23
+ # create_table :distributors do |t|
24
+ # t.string :zipcode
25
+ # end
26
+ #
27
+ # execute <<~SQL
28
+ # ALTER TABLE distributors
29
+ # ADD CONSTRAINT zipchk
30
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
31
+ # SQL
32
+ # end
33
+ # end
34
+ #
35
+ # There are two ways to mitigate this problem.
36
+ #
37
+ # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
38
+ #
39
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
40
+ # def up
41
+ # create_table :distributors do |t|
42
+ # t.string :zipcode
43
+ # end
44
+ #
45
+ # execute <<~SQL
46
+ # ALTER TABLE distributors
47
+ # ADD CONSTRAINT zipchk
48
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
49
+ # SQL
50
+ # end
51
+ #
52
+ # def down
53
+ # execute <<~SQL
54
+ # ALTER TABLE distributors
55
+ # DROP CONSTRAINT zipchk
56
+ # SQL
57
+ #
58
+ # drop_table :distributors
59
+ # end
60
+ # end
61
+ #
62
+ # 2. Use the #reversible method in <tt>#change</tt> method:
63
+ #
64
+ # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
65
+ # def change
66
+ # create_table :distributors do |t|
67
+ # t.string :zipcode
68
+ # end
69
+ #
70
+ # reversible do |dir|
71
+ # dir.up do
72
+ # execute <<~SQL
73
+ # ALTER TABLE distributors
74
+ # ADD CONSTRAINT zipchk
75
+ # CHECK (char_length(zipcode) = 5) NO INHERIT;
76
+ # SQL
77
+ # end
78
+ #
79
+ # dir.down do
80
+ # execute <<~SQL
81
+ # ALTER TABLE distributors
82
+ # DROP CONSTRAINT zipchk
83
+ # SQL
84
+ # end
85
+ # end
86
+ # end
87
+ # end
88
+ class IrreversibleMigration < MigrationError
89
+ end
90
+
91
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
92
+ def initialize(version = nil)
93
+ if version
94
+ super("Multiple migrations have the version number #{version}.")
95
+ else
96
+ super("Duplicate migration version error.")
97
+ end
98
+ end
99
+ end
100
+
101
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
102
+ def initialize(name = nil)
103
+ if name
104
+ super("Multiple migrations have the name #{name}.")
105
+ else
106
+ super("Duplicate migration name.")
107
+ end
108
+ end
109
+ end
110
+
111
+ class UnknownMigrationVersionError < MigrationError #:nodoc:
112
+ def initialize(version = nil)
113
+ if version
114
+ super("No migration with version number #{version}.")
115
+ else
116
+ super("Unknown migration version.")
117
+ end
118
+ end
9
119
  end
10
120
 
11
- class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
12
- def initialize(version)
13
- super("Multiple migrations have the version number #{version}")
121
+ class IllegalMigrationNameError < MigrationError #:nodoc:
122
+ def initialize(name = nil)
123
+ if name
124
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
125
+ else
126
+ super("Illegal name for migration.")
127
+ end
128
+ end
129
+ end
130
+
131
+ class PendingMigrationError < MigrationError #:nodoc:
132
+ include ActiveSupport::ActionableError
133
+
134
+ action "Run pending migrations" do
135
+ ActiveRecord::Tasks::DatabaseTasks.migrate
136
+ end
137
+
138
+ def initialize(message = nil)
139
+ if !message && defined?(Rails.env)
140
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
141
+ elsif !message
142
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate")
143
+ else
144
+ super
145
+ end
14
146
  end
15
147
  end
16
148
 
17
- class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
18
- def initialize(name)
19
- super("Multiple migrations have the name #{name}")
149
+ class ConcurrentMigrationError < MigrationError #:nodoc:
150
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
151
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
152
+
153
+ def initialize(message = DEFAULT_MESSAGE)
154
+ super
155
+ end
156
+ end
157
+
158
+ class NoEnvironmentInSchemaError < MigrationError #:nodoc:
159
+ def initialize
160
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n rails db:environment:set"
161
+ if defined?(Rails.env)
162
+ super("#{msg} RAILS_ENV=#{::Rails.env}")
163
+ else
164
+ super(msg)
165
+ end
20
166
  end
21
167
  end
22
168
 
23
- class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
24
- def initialize(version)
25
- super("No migration with version number #{version}")
169
+ class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
170
+ def initialize(env = "production")
171
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
172
+ msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
173
+ msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
174
+ super(msg)
26
175
  end
27
176
  end
28
177
 
29
- class IllegalMigrationNameError < ActiveRecordError#:nodoc:
30
- def initialize(name)
31
- super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
178
+ class EnvironmentMismatchError < ActiveRecordError
179
+ def initialize(current: nil, stored: nil)
180
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
181
+ msg << "You are running in `#{ current }` environment. "
182
+ msg << "If you are sure you want to continue, first set the environment using:\n\n"
183
+ msg << " rails db:environment:set"
184
+ if defined?(Rails.env)
185
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
186
+ else
187
+ super("#{msg}\n\n")
188
+ end
32
189
  end
33
190
  end
34
191
 
@@ -44,9 +201,9 @@ module ActiveRecord
44
201
  #
45
202
  # Example of a simple migration:
46
203
  #
47
- # class AddSsl < ActiveRecord::Migration
204
+ # class AddSsl < ActiveRecord::Migration[5.0]
48
205
  # def up
49
- # add_column :accounts, :ssl_enabled, :boolean, :default => 1
206
+ # add_column :accounts, :ssl_enabled, :boolean, default: true
50
207
  # end
51
208
  #
52
209
  # def down
@@ -58,13 +215,13 @@ module ActiveRecord
58
215
  # if you're backing out of the migration. It shows how all migrations have
59
216
  # two methods +up+ and +down+ that describes the transformations
60
217
  # required to implement or remove the migration. These methods can consist
61
- # of both the migration specific methods like add_column and remove_column,
218
+ # of both the migration specific methods like +add_column+ and +remove_column+,
62
219
  # but may also contain regular Ruby code for generating data needed for the
63
220
  # transformations.
64
221
  #
65
222
  # Example of a more complex migration that also needs to initialize data:
66
223
  #
67
- # class AddSystemSettings < ActiveRecord::Migration
224
+ # class AddSystemSettings < ActiveRecord::Migration[5.0]
68
225
  # def up
69
226
  # create_table :system_settings do |t|
70
227
  # t.string :name
@@ -74,9 +231,9 @@ module ActiveRecord
74
231
  # t.integer :position
75
232
  # end
76
233
  #
77
- # SystemSetting.create :name => "notice",
78
- # :label => "Use notice?",
79
- # :value => 1
234
+ # SystemSetting.create name: 'notice',
235
+ # label: 'Use notice?',
236
+ # value: 1
80
237
  # end
81
238
  #
82
239
  # def down
@@ -84,46 +241,90 @@ module ActiveRecord
84
241
  # end
85
242
  # end
86
243
  #
87
- # This migration first adds the system_settings table, then creates the very
244
+ # This migration first adds the +system_settings+ table, then creates the very
88
245
  # first row in it using the Active Record model that relies on the table. It
89
- # also uses the more advanced create_table syntax where you can specify a
246
+ # also uses the more advanced +create_table+ syntax where you can specify a
90
247
  # complete table schema in one block call.
91
248
  #
92
249
  # == Available transformations
93
250
  #
94
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
251
+ # === Creation
252
+ #
253
+ # * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
254
+ # table having its name as the lexical order of the first two
255
+ # arguments. See
256
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
257
+ # details.
258
+ # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
95
259
  # makes the table object available to a block that can then add columns to it,
96
- # following the same format as add_column. See example above. The options hash
260
+ # following the same format as +add_column+. See example above. The options hash
97
261
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
98
262
  # table definition.
99
- # * <tt>drop_table(name)</tt>: Drops the table called +name+.
100
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
101
- # to +new_name+.
102
263
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
103
264
  # to the table called +table_name+
104
265
  # named +column_name+ specified to be one of the following types:
105
266
  # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
106
267
  # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
107
268
  # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
108
- # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
269
+ # specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
109
270
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
110
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
271
+ # <tt>{ limit: 50, null: false }</tt>) -- see
111
272
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
112
- # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
113
- # a column but keeps the type and content.
114
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
115
- # the column to a different type using the same parameters as add_column.
116
- # * <tt>remove_column(table_name, column_names)</tt>: Removes the column listed in
117
- # +column_names+ from the table called +table_name+.
273
+ # * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
274
+ # foreign key. +from_table+ is the table with the key column, +to_table+ contains
275
+ # the referenced primary key.
118
276
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
119
277
  # with the name of the column. Other options include
120
278
  # <tt>:name</tt>, <tt>:unique</tt> (e.g.
121
- # <tt>{ :name => "users_name_index", :unique => true }</tt>) and <tt>:order</tt>
122
- # (e.g. { :order => {:name => :desc} }</tt>).
123
- # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
124
- # specified by +column_name+.
125
- # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
279
+ # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
280
+ # (e.g. <tt>{ order: { name: :desc } }</tt>).
281
+ # * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
282
+ # +reference_name_id+ by default an integer. See
283
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
284
+ # * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
285
+ # and +updated_at+) columns to +table_name+.
286
+ #
287
+ # === Modification
288
+ #
289
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
290
+ # the column to a different type using the same parameters as add_column.
291
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
292
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
293
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
294
+ # as +default_or_changes+ will make this change reversible in the migration.
295
+ # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
296
+ # Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
297
+ # indicates whether the value can be +NULL+. See
298
+ # ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
299
+ # details.
300
+ # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
301
+ # the table called +name+. It makes the table object available to a block that
302
+ # can then add/remove columns, indexes or foreign keys to it.
303
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
304
+ # a column but keeps the type and content.
305
+ # * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
306
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
307
+ # to +new_name+.
308
+ #
309
+ # === Deletion
310
+ #
311
+ # * <tt>drop_table(name)</tt>: Drops the table called +name+.
312
+ # * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
313
+ # specified by the given arguments.
314
+ # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
315
+ # named +column_name+ from the table called +table_name+.
316
+ # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
317
+ # columns from the table definition.
318
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
319
+ # given foreign key from the table called +table_name+.
320
+ # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
321
+ # specified by +column_names+.
322
+ # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
126
323
  # specified by +index_name+.
324
+ # * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
325
+ # reference(s) on +table_name+ specified by +ref_name+.
326
+ # * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
327
+ # columns (+created_at+ and +updated_at+) from the table definition.
127
328
  #
128
329
  # == Irreversible transformations
129
330
  #
@@ -143,47 +344,43 @@ module ActiveRecord
143
344
  # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
144
345
  # UTC formatted date and time that the migration was generated.
145
346
  #
146
- # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
147
- # MyNewMigration.
148
- #
149
347
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
150
348
  #
151
349
  # rails generate migration add_fieldname_to_tablename fieldname:string
152
350
  #
153
- # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
154
- # class AddFieldnameToTablename < ActiveRecord::Migration
155
- # def up
351
+ # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
352
+ # class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
353
+ # def change
156
354
  # add_column :tablenames, :fieldname, :string
157
355
  # end
158
- #
159
- # def down
160
- # remove_column :tablenames, :fieldname
161
- # end
162
356
  # end
163
357
  #
164
358
  # To run migrations against the currently configured database, use
165
- # <tt>rake db:migrate</tt>. This will update the database by running all of the
359
+ # <tt>rails db:migrate</tt>. This will update the database by running all of the
166
360
  # pending migrations, creating the <tt>schema_migrations</tt> table
167
361
  # (see "About the schema_migrations table" section below) if missing. It will also
168
- # invoke the db:schema:dump task, which will update your db/schema.rb file
362
+ # invoke the db:schema:dump command, which will update your db/schema.rb file
169
363
  # to match the structure of your database.
170
364
  #
171
365
  # To roll the database back to a previous migration version, use
172
- # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
173
- # you wish to downgrade. If any of the migrations throw an
174
- # <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
175
- # have some manual work to do.
366
+ # <tt>rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
367
+ # you wish to downgrade. Alternatively, you can also use the STEP option if you
368
+ # wish to rollback last few migrations. <tt>rails db:rollback STEP=2</tt> will rollback
369
+ # the latest two migrations.
370
+ #
371
+ # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
372
+ # that step will fail and you'll have some manual work to do.
176
373
  #
177
374
  # == Database support
178
375
  #
179
376
  # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
180
- # SQL Server, Sybase, and Oracle (all supported databases except DB2).
377
+ # SQL Server, and Oracle (all supported databases except DB2).
181
378
  #
182
379
  # == More examples
183
380
  #
184
381
  # Not all migrations change the schema. Some just fix the data:
185
382
  #
186
- # class RemoveEmptyTags < ActiveRecord::Migration
383
+ # class RemoveEmptyTags < ActiveRecord::Migration[5.0]
187
384
  # def up
188
385
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
189
386
  # end
@@ -196,7 +393,7 @@ module ActiveRecord
196
393
  #
197
394
  # Others remove columns when they migrate up instead of down:
198
395
  #
199
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
396
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
200
397
  # def up
201
398
  # remove_column :items, :incomplete_items_count
202
399
  # remove_column :items, :completed_items_count
@@ -210,7 +407,7 @@ module ActiveRecord
210
407
  #
211
408
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
212
409
  #
213
- # class MakeJoinUnique < ActiveRecord::Migration
410
+ # class MakeJoinUnique < ActiveRecord::Migration[5.0]
214
411
  # def up
215
412
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
216
413
  # end
@@ -227,7 +424,7 @@ module ActiveRecord
227
424
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
228
425
  # latest column data from after the new column was added. Example:
229
426
  #
230
- # class AddPeopleSalary < ActiveRecord::Migration
427
+ # class AddPeopleSalary < ActiveRecord::Migration[5.0]
231
428
  # def up
232
429
  # add_column :people, :salary, :integer
233
430
  # Person.reset_column_information
@@ -261,21 +458,6 @@ module ActiveRecord
261
458
  # The phrase "Updating salaries..." would then be printed, along with the
262
459
  # benchmark for the block when the block completes.
263
460
  #
264
- # == About the schema_migrations table
265
- #
266
- # Rails versions 2.0 and prior used to create a table called
267
- # <tt>schema_info</tt> when using migrations. This table contained the
268
- # version of the schema as of the last applied migration.
269
- #
270
- # Starting with Rails 2.1, the <tt>schema_info</tt> table is
271
- # (automatically) replaced by the <tt>schema_migrations</tt> table, which
272
- # contains the version numbers of all the migrations applied.
273
- #
274
- # As a result, it is now possible to add migration files that are numbered
275
- # lower than the current schema version: when migrating up, those
276
- # never-applied "interleaved" migrations will be automatically applied, and
277
- # when migrating down, never-applied "interleaved" migrations will be skipped.
278
- #
279
461
  # == Timestamped Migrations
280
462
  #
281
463
  # By default, Rails generates migrations that look like:
@@ -293,15 +475,14 @@ module ActiveRecord
293
475
  #
294
476
  # == Reversible Migrations
295
477
  #
296
- # Starting with Rails 3.1, you will be able to define reversible migrations.
297
478
  # Reversible migrations are migrations that know how to go +down+ for you.
298
- # You simply supply the +up+ logic, and the Migration system will figure out
479
+ # You simply supply the +up+ logic, and the Migration system figures out
299
480
  # how to execute the down commands for you.
300
481
  #
301
482
  # To define a reversible migration, define the +change+ method in your
302
483
  # migration like this:
303
484
  #
304
- # class TenderloveMigration < ActiveRecord::Migration
485
+ # class TenderloveMigration < ActiveRecord::Migration[5.0]
305
486
  # def change
306
487
  # create_table(:horses) do |t|
307
488
  # t.column :content, :text
@@ -313,9 +494,9 @@ module ActiveRecord
313
494
  # This migration will create the horses table for you on the way up, and
314
495
  # automatically figure out how to drop the table on the way down.
315
496
  #
316
- # Some commands like +remove_column+ cannot be reversed. If you care to
317
- # define how to move up and down in these cases, you should define the +up+
318
- # and +down+ methods as before.
497
+ # Some commands cannot be reversed. If you care to define how to move up
498
+ # and down in these cases, you should define the +up+ and +down+ methods
499
+ # as before.
319
500
  #
320
501
  # If a command cannot be reversed, an
321
502
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -323,45 +504,285 @@ module ActiveRecord
323
504
  #
324
505
  # For a list of commands that are reversible, please see
325
506
  # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
507
+ #
508
+ # == Transactional Migrations
509
+ #
510
+ # If the database adapter supports DDL transactions, all migrations will
511
+ # automatically be wrapped in a transaction. There are queries that you
512
+ # can't execute inside a transaction though, and for these situations
513
+ # you can turn the automatic transactions off.
514
+ #
515
+ # class ChangeEnum < ActiveRecord::Migration[5.0]
516
+ # disable_ddl_transaction!
517
+ #
518
+ # def up
519
+ # execute "ALTER TYPE model_size ADD VALUE 'new_value'"
520
+ # end
521
+ # end
522
+ #
523
+ # Remember that you can still open your own transactions, even if you
524
+ # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
326
525
  class Migration
327
- autoload :CommandRecorder, 'active_record/migration/command_recorder'
526
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
527
+ autoload :Compatibility, "active_record/migration/compatibility"
328
528
 
329
- class << self
330
- attr_accessor :delegate # :nodoc:
529
+ # This must be defined before the inherited hook, below
530
+ class Current < Migration #:nodoc:
531
+ end
532
+
533
+ def self.inherited(subclass) #:nodoc:
534
+ super
535
+ if subclass.superclass == Migration
536
+ raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
537
+ "Please specify the Rails release the migration was written for:\n" \
538
+ "\n" \
539
+ " class #{subclass} < ActiveRecord::Migration[4.2]"
540
+ end
331
541
  end
332
542
 
333
- def self.method_missing(name, *args, &block) # :nodoc:
334
- (delegate || superclass.delegate).send(name, *args, &block)
543
+ def self.[](version)
544
+ Compatibility.find(version)
335
545
  end
336
546
 
337
- def self.migrate(direction)
338
- new.migrate direction
547
+ def self.current_version
548
+ ActiveRecord::VERSION::STRING.to_f
339
549
  end
340
550
 
341
- cattr_accessor :verbose
551
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
552
+
553
+ # This class is used to verify that all migrations have been run before
554
+ # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
555
+ class CheckPending
556
+ def initialize(app)
557
+ @app = app
558
+ @last_check = 0
559
+ end
560
+
561
+ def call(env)
562
+ mtime = ActiveRecord::Base.connection.migration_context.last_migration.mtime.to_i
563
+ if @last_check < mtime
564
+ ActiveRecord::Migration.check_pending!(connection)
565
+ @last_check = mtime
566
+ end
567
+ @app.call(env)
568
+ end
569
+
570
+ private
571
+
572
+ def connection
573
+ ActiveRecord::Base.connection
574
+ end
575
+ end
576
+
577
+ class << self
578
+ attr_accessor :delegate #:nodoc:
579
+ attr_accessor :disable_ddl_transaction #:nodoc:
580
+
581
+ def nearest_delegate #:nodoc:
582
+ delegate || superclass.nearest_delegate
583
+ end
584
+
585
+ # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
586
+ def check_pending!(connection = Base.connection)
587
+ raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
588
+ end
342
589
 
590
+ def load_schema_if_pending!
591
+ current_config = Base.connection_config
592
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
593
+
594
+ needs_update = !all_configs.all? do |db_config|
595
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
596
+ end
597
+
598
+ if needs_update
599
+ # Roundtrip to Rake to allow plugins to hook into database initialization.
600
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
601
+ FileUtils.cd(root) do
602
+ Base.clear_all_connections!
603
+ system("bin/rails db:test:prepare")
604
+ end
605
+ end
606
+
607
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
608
+ Base.establish_connection(current_config)
609
+
610
+ check_pending!
611
+ end
612
+
613
+ def maintain_test_schema! #:nodoc:
614
+ if ActiveRecord::Base.maintain_test_schema
615
+ suppress_messages { load_schema_if_pending! }
616
+ end
617
+ end
618
+
619
+ def method_missing(name, *args, &block) #:nodoc:
620
+ nearest_delegate.send(name, *args, &block)
621
+ end
622
+
623
+ def migrate(direction)
624
+ new.migrate direction
625
+ end
626
+
627
+ # Disable the transaction wrapping this migration.
628
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
629
+ #
630
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
631
+ def disable_ddl_transaction!
632
+ @disable_ddl_transaction = true
633
+ end
634
+ end
635
+
636
+ def disable_ddl_transaction #:nodoc:
637
+ self.class.disable_ddl_transaction
638
+ end
639
+
640
+ cattr_accessor :verbose
343
641
  attr_accessor :name, :version
344
642
 
345
- def initialize
346
- @name = self.class.name
347
- @version = nil
643
+ def initialize(name = self.class.name, version = nil)
644
+ @name = name
645
+ @version = version
348
646
  @connection = nil
349
- @reverting = false
350
647
  end
351
648
 
649
+ self.verbose = true
352
650
  # instantiate the delegate object after initialize is defined
353
- self.verbose = true
354
651
  self.delegate = new
355
652
 
356
- def revert
357
- @reverting = true
358
- yield
359
- ensure
360
- @reverting = false
653
+ # Reverses the migration commands for the given block and
654
+ # the given migrations.
655
+ #
656
+ # The following migration will remove the table 'horses'
657
+ # and create the table 'apples' on the way up, and the reverse
658
+ # on the way down.
659
+ #
660
+ # class FixTLMigration < ActiveRecord::Migration[5.0]
661
+ # def change
662
+ # revert do
663
+ # create_table(:horses) do |t|
664
+ # t.text :content
665
+ # t.datetime :remind_at
666
+ # end
667
+ # end
668
+ # create_table(:apples) do |t|
669
+ # t.string :variety
670
+ # end
671
+ # end
672
+ # end
673
+ #
674
+ # Or equivalently, if +TenderloveMigration+ is defined as in the
675
+ # documentation for Migration:
676
+ #
677
+ # require_relative '20121212123456_tenderlove_migration'
678
+ #
679
+ # class FixupTLMigration < ActiveRecord::Migration[5.0]
680
+ # def change
681
+ # revert TenderloveMigration
682
+ #
683
+ # create_table(:apples) do |t|
684
+ # t.string :variety
685
+ # end
686
+ # end
687
+ # end
688
+ #
689
+ # This command can be nested.
690
+ def revert(*migration_classes)
691
+ run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
692
+ if block_given?
693
+ if connection.respond_to? :revert
694
+ connection.revert { yield }
695
+ else
696
+ recorder = command_recorder
697
+ @connection = recorder
698
+ suppress_messages do
699
+ connection.revert { yield }
700
+ end
701
+ @connection = recorder.delegate
702
+ recorder.replay(self)
703
+ end
704
+ end
361
705
  end
362
706
 
363
707
  def reverting?
364
- @reverting
708
+ connection.respond_to?(:reverting) && connection.reverting
709
+ end
710
+
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
712
+ def up
713
+ yield unless reverting
714
+ end
715
+
716
+ def down
717
+ yield if reverting
718
+ end
719
+ end
720
+
721
+ # Used to specify an operation that can be run in one direction or another.
722
+ # Call the methods +up+ and +down+ of the yielded object to run a block
723
+ # only in one given direction.
724
+ # The whole block will be called in the right order within the migration.
725
+ #
726
+ # In the following example, the looping on users will always be done
727
+ # when the three columns 'first_name', 'last_name' and 'full_name' exist,
728
+ # even when migrating down:
729
+ #
730
+ # class SplitNameMigration < ActiveRecord::Migration[5.0]
731
+ # def change
732
+ # add_column :users, :first_name, :string
733
+ # add_column :users, :last_name, :string
734
+ #
735
+ # reversible do |dir|
736
+ # User.reset_column_information
737
+ # User.all.each do |u|
738
+ # dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
739
+ # dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
740
+ # u.save
741
+ # end
742
+ # end
743
+ #
744
+ # revert { add_column :users, :full_name, :string }
745
+ # end
746
+ # end
747
+ def reversible
748
+ helper = ReversibleBlockHelper.new(reverting?)
749
+ execute_block { yield helper }
750
+ end
751
+
752
+ # Used to specify an operation that is only run when migrating up
753
+ # (for example, populating a new column with its initial values).
754
+ #
755
+ # In the following example, the new column +published+ will be given
756
+ # the value +true+ for all existing records.
757
+ #
758
+ # class AddPublishedToPosts < ActiveRecord::Migration[5.2]
759
+ # def change
760
+ # add_column :posts, :published, :boolean, default: false
761
+ # up_only do
762
+ # execute "update posts set published = 'true'"
763
+ # end
764
+ # end
765
+ # end
766
+ def up_only
767
+ execute_block { yield } unless reverting?
768
+ end
769
+
770
+ # Runs the given migration classes.
771
+ # Last argument can specify options:
772
+ # - :direction (default is :up)
773
+ # - :revert (default is false)
774
+ def run(*migration_classes)
775
+ opts = migration_classes.extract_options!
776
+ dir = opts[:direction] || :up
777
+ dir = (dir == :down ? :up : :down) if opts[:revert]
778
+ if reverting?
779
+ # If in revert and going :up, say, we want to execute :down without reverting, so
780
+ revert { run(*migration_classes, direction: dir, revert: true) }
781
+ else
782
+ migration_classes.each do |migration_class|
783
+ migration_class.new.exec_migration(connection, dir)
784
+ end
785
+ end
365
786
  end
366
787
 
367
788
  def up
@@ -385,31 +806,11 @@ module ActiveRecord
385
806
  when :down then announce "reverting"
386
807
  end
387
808
 
388
- time = nil
809
+ time = nil
389
810
  ActiveRecord::Base.connection_pool.with_connection do |conn|
390
- @connection = conn
391
- if respond_to?(:change)
392
- if direction == :down
393
- recorder = CommandRecorder.new(@connection)
394
- suppress_messages do
395
- @connection = recorder
396
- change
397
- end
398
- @connection = conn
399
- time = Benchmark.measure {
400
- self.revert {
401
- recorder.inverse.each do |cmd, args|
402
- send(cmd, *args)
403
- end
404
- }
405
- }
406
- else
407
- time = Benchmark.measure { change }
408
- end
409
- else
410
- time = Benchmark.measure { send(direction) }
811
+ time = Benchmark.measure do
812
+ exec_migration(conn, direction)
411
813
  end
412
- @connection = nil
413
814
  end
414
815
 
415
816
  case direction
@@ -418,7 +819,22 @@ module ActiveRecord
418
819
  end
419
820
  end
420
821
 
421
- def write(text="")
822
+ def exec_migration(conn, direction)
823
+ @connection = conn
824
+ if respond_to?(:change)
825
+ if direction == :down
826
+ revert { change }
827
+ else
828
+ change
829
+ end
830
+ else
831
+ send(direction)
832
+ end
833
+ ensure
834
+ @connection = nil
835
+ end
836
+
837
+ def write(text = "")
422
838
  puts(text) if verbose
423
839
  end
424
840
 
@@ -428,10 +844,14 @@ module ActiveRecord
428
844
  write "== %s %s" % [text, "=" * length]
429
845
  end
430
846
 
431
- def say(message, subitem=false)
847
+ # Takes a message argument and outputs it as is.
848
+ # A second boolean argument can be passed to specify whether to indent or not.
849
+ def say(message, subitem = false)
432
850
  write "#{subitem ? " ->" : "--"} #{message}"
433
851
  end
434
852
 
853
+ # Outputs text along with how long it took to run its block.
854
+ # If the block returns an integer it assumes it is the number of rows affected.
435
855
  def say_with_time(message)
436
856
  say(message)
437
857
  result = nil
@@ -441,6 +861,7 @@ module ActiveRecord
441
861
  result
442
862
  end
443
863
 
864
+ # Takes a block as an argument and suppresses any output generated by the block.
444
865
  def suppress_messages
445
866
  save, self.verbose = verbose, false
446
867
  yield
@@ -453,13 +874,16 @@ module ActiveRecord
453
874
  end
454
875
 
455
876
  def method_missing(method, *arguments, &block)
456
- arg_list = arguments.map{ |a| a.inspect } * ', '
877
+ arg_list = arguments.map(&:inspect) * ", "
457
878
 
458
879
  say_with_time "#{method}(#{arg_list})" do
459
- unless reverting?
460
- unless arguments.empty? || method == :execute
461
- arguments[0] = Migrator.proper_table_name(arguments.first)
462
- arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
880
+ unless connection.respond_to? :revert
881
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
882
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
883
+ if [:rename_table, :add_foreign_key].include?(method) ||
884
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
885
+ arguments[1] = proper_table_name(arguments.second, table_name_options)
886
+ end
463
887
  end
464
888
  end
465
889
  return super unless connection.respond_to?(method)
@@ -469,17 +893,29 @@ module ActiveRecord
469
893
 
470
894
  def copy(destination, sources, options = {})
471
895
  copied = []
896
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
472
897
 
473
- FileUtils.mkdir_p(destination) unless File.exists?(destination)
898
+ FileUtils.mkdir_p(destination) unless File.exist?(destination)
474
899
 
475
- destination_migrations = ActiveRecord::Migrator.migrations(destination)
900
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
476
901
  last = destination_migrations.last
477
902
  sources.each do |scope, path|
478
- source_migrations = ActiveRecord::Migrator.migrations(path)
903
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
479
904
 
480
905
  source_migrations.each do |migration|
481
- source = File.read(migration.filename)
482
- source = "# This migration comes from #{scope} (originally #{migration.version})\n#{source}"
906
+ source = File.binread(migration.filename)
907
+ inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
908
+ magic_comments = +""
909
+ loop do
910
+ # If we have a magic comment in the original migration,
911
+ # insert our comment after the first newline(end of the magic comment line)
912
+ # so the magic keep working.
913
+ # Note that magic comments must be at the first line(except sh-bang).
914
+ source.sub!(/\A(?:#.*\b(?:en)?coding:\s*\S+|#\s*frozen_string_literal:\s*(?:true|false)).*\n/) do |magic_comment|
915
+ magic_comments << magic_comment; ""
916
+ end || break
917
+ end
918
+ source = "#{magic_comments}#{inserted_comment}#{source}"
483
919
 
484
920
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
485
921
  if options[:on_skip] && duplicate.scope != scope.to_s
@@ -493,7 +929,7 @@ module ActiveRecord
493
929
  old_path, migration.filename = migration.filename, new_path
494
930
  last = migration
495
931
 
496
- File.open(migration.filename, "w") { |f| f.write source }
932
+ File.binwrite(migration.filename, source)
497
933
  copied << migration
498
934
  options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
499
935
  destination_migrations << migration
@@ -503,19 +939,52 @@ module ActiveRecord
503
939
  copied
504
940
  end
505
941
 
942
+ # Finds the correct table name given an Active Record object.
943
+ # Uses the Active Record object's own table_name, or pre/suffix from the
944
+ # options passed in.
945
+ def proper_table_name(name, options = {})
946
+ if name.respond_to? :table_name
947
+ name.table_name
948
+ else
949
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
950
+ end
951
+ end
952
+
953
+ # Determines the version number of the next migration.
506
954
  def next_migration_number(number)
507
955
  if ActiveRecord::Base.timestamped_migrations
508
956
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
509
957
  else
510
- "%.3d" % number
958
+ SchemaMigration.normalize_migration_number(number)
511
959
  end
512
960
  end
961
+
962
+ # Builds a hash for use in ActiveRecord::Migration#proper_table_name using
963
+ # the Active Record object's table_name prefix and suffix
964
+ def table_name_options(config = ActiveRecord::Base) #:nodoc:
965
+ {
966
+ table_name_prefix: config.table_name_prefix,
967
+ table_name_suffix: config.table_name_suffix
968
+ }
969
+ end
970
+
971
+ private
972
+ def execute_block
973
+ if connection.respond_to? :execute_block
974
+ super # use normal delegation to record the block
975
+ else
976
+ yield
977
+ end
978
+ end
979
+
980
+ def command_recorder
981
+ CommandRecorder.new(connection)
982
+ end
513
983
  end
514
984
 
515
985
  # MigrationProxy is used to defer loading of the actual migration classes
516
986
  # until they are needed
517
- class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
518
-
987
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
519
988
  def initialize(name, version, filename, scope)
520
989
  super
521
990
  @migration = nil
@@ -525,7 +994,11 @@ module ActiveRecord
525
994
  File.basename(filename)
526
995
  end
527
996
 
528
- delegate :migrate, :announce, :write, :to => :migration
997
+ def mtime
998
+ File.mtime filename
999
+ end
1000
+
1001
+ delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
529
1002
 
530
1003
  private
531
1004
 
@@ -535,229 +1008,343 @@ module ActiveRecord
535
1008
 
536
1009
  def load_migration
537
1010
  require(File.expand_path(filename))
538
- name.constantize.new
1011
+ name.constantize.new(name, version)
539
1012
  end
1013
+ end
1014
+
1015
+ class NullMigration < MigrationProxy #:nodoc:
1016
+ def initialize
1017
+ super(nil, 0, nil, nil)
1018
+ end
540
1019
 
1020
+ def mtime
1021
+ 0
1022
+ end
541
1023
  end
542
1024
 
543
- class Migrator#:nodoc:
544
- class << self
545
- attr_writer :migrations_paths
546
- alias :migrations_path= :migrations_paths=
547
-
548
- def migrate(migrations_paths, target_version = nil, &block)
549
- case
550
- when target_version.nil?
551
- up(migrations_paths, target_version, &block)
552
- when current_version == 0 && target_version == 0
553
- []
554
- when current_version > target_version
555
- down(migrations_paths, target_version, &block)
556
- else
557
- up(migrations_paths, target_version, &block)
558
- end
559
- end
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
560
1027
 
561
- def rollback(migrations_paths, steps=1)
562
- move(:down, migrations_paths, steps)
563
- end
1028
+ def initialize(migrations_paths, schema_migration)
1029
+ @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1031
+ end
564
1032
 
565
- def forward(migrations_paths, steps=1)
566
- move(:up, migrations_paths, steps)
1033
+ def migrate(target_version = nil, &block)
1034
+ case
1035
+ when target_version.nil?
1036
+ up(target_version, &block)
1037
+ when current_version == 0 && target_version == 0
1038
+ []
1039
+ when current_version > target_version
1040
+ down(target_version, &block)
1041
+ else
1042
+ up(target_version, &block)
567
1043
  end
1044
+ end
568
1045
 
569
- def up(migrations_paths, target_version = nil, &block)
570
- self.new(:up, migrations_paths, target_version).migrate(&block)
571
- end
1046
+ def rollback(steps = 1)
1047
+ move(:down, steps)
1048
+ end
572
1049
 
573
- def down(migrations_paths, target_version = nil, &block)
574
- self.new(:down, migrations_paths, target_version).migrate(&block)
575
- end
1050
+ def forward(steps = 1)
1051
+ move(:up, steps)
1052
+ end
576
1053
 
577
- def run(direction, migrations_paths, target_version)
578
- self.new(direction, migrations_paths, target_version).run
1054
+ def up(target_version = nil)
1055
+ selected_migrations = if block_given?
1056
+ migrations.select { |m| yield m }
1057
+ else
1058
+ migrations
579
1059
  end
580
1060
 
581
- def schema_migrations_table_name
582
- Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
583
- end
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1062
+ end
584
1063
 
585
- def get_all_versions
586
- table = Arel::Table.new(schema_migrations_table_name)
587
- Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
1064
+ def down(target_version = nil)
1065
+ selected_migrations = if block_given?
1066
+ migrations.select { |m| yield m }
1067
+ else
1068
+ migrations
588
1069
  end
589
1070
 
590
- def current_version
591
- sm_table = schema_migrations_table_name
592
- if Base.connection.table_exists?(sm_table)
593
- get_all_versions.max || 0
594
- else
595
- 0
596
- end
597
- end
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1072
+ end
598
1073
 
599
- def proper_table_name(name)
600
- # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
601
- name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
602
- end
1074
+ def run(direction, target_version)
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1076
+ end
1077
+
1078
+ def open
1079
+ Migrator.new(:up, migrations, schema_migration)
1080
+ end
603
1081
 
604
- def migrations_paths
605
- @migrations_paths ||= ['db/migrate']
606
- # just to not break things if someone uses: migration_path = some_string
607
- Array.wrap(@migrations_paths)
1082
+ def get_all_versions
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1085
+ else
1086
+ []
608
1087
  end
1088
+ end
1089
+
1090
+ def current_version
1091
+ get_all_versions.max || 0
1092
+ rescue ActiveRecord::NoDatabaseError
1093
+ end
1094
+
1095
+ def needs_migration?
1096
+ (migrations.collect(&:version) - get_all_versions).size > 0
1097
+ end
609
1098
 
610
- def migrations_path
611
- migrations_paths.first
1099
+ def any_migrations?
1100
+ migrations.any?
1101
+ end
1102
+
1103
+ def last_migration #:nodoc:
1104
+ migrations.last || NullMigration.new
1105
+ end
1106
+
1107
+ def migrations
1108
+ migrations = migration_files.map do |file|
1109
+ version, name, scope = parse_migration_filename(file)
1110
+ raise IllegalMigrationNameError.new(file) unless version
1111
+ version = version.to_i
1112
+ name = name.camelize
1113
+
1114
+ MigrationProxy.new(name, version, file, scope)
612
1115
  end
613
1116
 
614
- def migrations(paths, *args)
615
- if args.empty?
616
- subdirectories = true
617
- else
618
- subdirectories = args.first
619
- ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
620
- end
1117
+ migrations.sort_by(&:version)
1118
+ end
621
1119
 
622
- paths = Array.wrap(paths)
1120
+ def migrations_status
1121
+ db_list = schema_migration.normalized_versions
623
1122
 
624
- glob = subdirectories ? "**/" : ""
625
- files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
1123
+ file_list = migration_files.map do |file|
1124
+ version, name, scope = parse_migration_filename(file)
1125
+ raise IllegalMigrationNameError.new(file) unless version
1126
+ version = schema_migration.normalize_migration_number(version)
1127
+ status = db_list.delete(version) ? "up" : "down"
1128
+ [status, version, (name + scope).humanize]
1129
+ end.compact
626
1130
 
627
- seen = Hash.new false
1131
+ db_list.map! do |version|
1132
+ ["up", version, "********** NO FILE **********"]
1133
+ end
628
1134
 
629
- migrations = files.map do |file|
630
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
1135
+ (db_list + file_list).sort_by { |_, version, _| version }
1136
+ end
631
1137
 
632
- raise IllegalMigrationNameError.new(file) unless version
633
- version = version.to_i
634
- name = name.camelize
1138
+ def current_environment
1139
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1140
+ end
635
1141
 
636
- raise DuplicateMigrationVersionError.new(version) if seen[version]
637
- raise DuplicateMigrationNameError.new(name) if seen[name]
1142
+ def protected_environment?
1143
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1144
+ end
638
1145
 
639
- seen[version] = seen[name] = true
1146
+ def last_stored_environment
1147
+ return nil if current_version == 0
1148
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
640
1149
 
641
- MigrationProxy.new(name, version, file, scope)
642
- end
1150
+ environment = ActiveRecord::InternalMetadata[:environment]
1151
+ raise NoEnvironmentInSchemaError unless environment
1152
+ environment
1153
+ end
643
1154
 
644
- migrations.sort_by(&:version)
1155
+ private
1156
+ def migration_files
1157
+ paths = Array(migrations_paths)
1158
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
645
1159
  end
646
1160
 
647
- private
1161
+ def parse_migration_filename(filename)
1162
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1163
+ end
648
1164
 
649
- def move(direction, migrations_paths, steps)
650
- migrator = self.new(direction, migrations_paths)
651
- start_index = migrator.migrations.index(migrator.current_migration)
1165
+ def move(direction, steps)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
652
1167
 
653
- if start_index
654
- finish = migrator.migrations[start_index + steps]
655
- version = finish ? finish.version : 0
656
- send(direction, migrations_paths, version)
1168
+ if current_version != 0 && !migrator.current_migration
1169
+ raise UnknownMigrationVersionError.new(current_version)
657
1170
  end
1171
+
1172
+ start_index =
1173
+ if current_version == 0
1174
+ 0
1175
+ else
1176
+ migrator.migrations.index(migrator.current_migration)
1177
+ end
1178
+
1179
+ finish = migrator.migrations[start_index + steps]
1180
+ version = finish ? finish.version : 0
1181
+ send(direction, version)
1182
+ end
1183
+ end
1184
+
1185
+ class Migrator # :nodoc:
1186
+ class << self
1187
+ attr_accessor :migrations_paths
1188
+
1189
+ # For cases where a table doesn't exist like loading from schema cache
1190
+ def current_version
1191
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
658
1192
  end
659
1193
  end
660
1194
 
661
- def initialize(direction, migrations_paths, target_version = nil)
662
- raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
663
- Base.connection.initialize_schema_migrations_table
664
- @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
1195
+ self.migrations_paths = ["db/migrate"]
1196
+
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1198
+ @direction = direction
1199
+ @target_version = target_version
1200
+ @migrated_versions = nil
1201
+ @migrations = migrations
1202
+ @schema_migration = schema_migration
1203
+
1204
+ validate(@migrations)
1205
+
1206
+ @schema_migration.create_table
1207
+ ActiveRecord::InternalMetadata.create_table
665
1208
  end
666
1209
 
667
1210
  def current_version
668
- migrated.last || 0
1211
+ migrated.max || 0
669
1212
  end
670
1213
 
671
1214
  def current_migration
672
1215
  migrations.detect { |m| m.version == current_version }
673
1216
  end
1217
+ alias :current :current_migration
674
1218
 
675
1219
  def run
676
- target = migrations.detect { |m| m.version == @target_version }
677
- raise UnknownMigrationVersionError.new(@target_version) if target.nil?
678
- unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
679
- target.migrate(@direction)
680
- record_version_state_after_migrating(target.version)
1220
+ if use_advisory_lock?
1221
+ with_advisory_lock { run_without_lock }
1222
+ else
1223
+ run_without_lock
681
1224
  end
682
1225
  end
683
1226
 
684
- def migrate(&block)
685
- current = migrations.detect { |m| m.version == current_version }
686
- target = migrations.detect { |m| m.version == @target_version }
687
-
688
- if target.nil? && @target_version && @target_version > 0
689
- raise UnknownMigrationVersionError.new(@target_version)
1227
+ def migrate
1228
+ if use_advisory_lock?
1229
+ with_advisory_lock { migrate_without_lock }
1230
+ else
1231
+ migrate_without_lock
690
1232
  end
1233
+ end
691
1234
 
692
- start = up? ? 0 : (migrations.index(current) || 0)
693
- finish = migrations.index(target) || migrations.size - 1
1235
+ def runnable
694
1236
  runnable = migrations[start..finish]
1237
+ if up?
1238
+ runnable.reject { |m| ran?(m) }
1239
+ else
1240
+ # skip the last migration if we're headed down, but not ALL the way down
1241
+ runnable.pop if target
1242
+ runnable.find_all { |m| ran?(m) }
1243
+ end
1244
+ end
1245
+
1246
+ def migrations
1247
+ down? ? @migrations.reverse : @migrations.sort_by(&:version)
1248
+ end
695
1249
 
696
- # skip the last migration if we're headed down, but not ALL the way down
697
- runnable.pop if down? && target
1250
+ def pending_migrations
1251
+ already_migrated = migrated
1252
+ migrations.reject { |m| already_migrated.include?(m.version) }
1253
+ end
698
1254
 
699
- ran = []
700
- runnable.each do |migration|
701
- if block && !block.call(migration)
702
- next
703
- end
1255
+ def migrated
1256
+ @migrated_versions || load_migrated
1257
+ end
704
1258
 
705
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1259
+ def load_migrated
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1261
+ end
1262
+
1263
+ private
706
1264
 
707
- seen = migrated.include?(migration.version.to_i)
1265
+ # Used for running a specific migration.
1266
+ def run_without_lock
1267
+ migration = migrations.detect { |m| m.version == @target_version }
1268
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1269
+ result = execute_migration_in_transaction(migration, @direction)
708
1270
 
709
- # On our way up, we skip migrating the ones we've already migrated
710
- next if up? && seen
1271
+ record_environment
1272
+ result
1273
+ end
711
1274
 
712
- # On our way down, we skip reverting the ones we've never migrated
713
- if down? && !seen
714
- migration.announce 'never migrated, skipping'; migration.write
715
- next
1275
+ # Used for running multiple migrations up to or down to a certain value.
1276
+ def migrate_without_lock
1277
+ if invalid_target?
1278
+ raise UnknownMigrationVersionError.new(@target_version)
716
1279
  end
717
1280
 
718
- begin
719
- ddl_transaction do
720
- migration.migrate(@direction)
721
- record_version_state_after_migrating(migration.version)
722
- end
723
- ran << migration
724
- rescue => e
725
- canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
726
- raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
1281
+ result = runnable.each do |migration|
1282
+ execute_migration_in_transaction(migration, @direction)
727
1283
  end
1284
+
1285
+ record_environment
1286
+ result
728
1287
  end
729
- ran
730
- end
731
1288
 
732
- def migrations
733
- @migrations ||= begin
734
- migrations = self.class.migrations(@migrations_paths)
735
- down? ? migrations.reverse : migrations
1289
+ # Stores the current environment in the database.
1290
+ def record_environment
1291
+ return if down?
1292
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.migration_context.current_environment
736
1293
  end
737
- end
738
1294
 
739
- def pending_migrations
740
- already_migrated = migrated
741
- migrations.reject { |m| already_migrated.include?(m.version.to_i) }
742
- end
1295
+ def ran?(migration)
1296
+ migrated.include?(migration.version.to_i)
1297
+ end
743
1298
 
744
- def migrated
745
- @migrated_versions ||= self.class.get_all_versions
746
- end
1299
+ # Return true if a valid version is not provided.
1300
+ def invalid_target?
1301
+ @target_version && @target_version != 0 && !target
1302
+ end
747
1303
 
748
- private
749
- def record_version_state_after_migrating(version)
750
- table = Arel::Table.new(self.class.schema_migrations_table_name)
1304
+ def execute_migration_in_transaction(migration, direction)
1305
+ return if down? && !migrated.include?(migration.version.to_i)
1306
+ return if up? && migrated.include?(migration.version.to_i)
1307
+
1308
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1309
+
1310
+ ddl_transaction(migration) do
1311
+ migration.migrate(direction)
1312
+ record_version_state_after_migrating(migration.version)
1313
+ end
1314
+ rescue => e
1315
+ msg = +"An error has occurred, "
1316
+ msg << "this and " if use_transaction?(migration)
1317
+ msg << "all later migrations canceled:\n\n#{e}"
1318
+ raise StandardError, msg, e.backtrace
1319
+ end
1320
+
1321
+ def target
1322
+ migrations.detect { |m| m.version == @target_version }
1323
+ end
1324
+
1325
+ def finish
1326
+ migrations.index(target) || migrations.size - 1
1327
+ end
1328
+
1329
+ def start
1330
+ up? ? 0 : (migrations.index(current) || 0)
1331
+ end
1332
+
1333
+ def validate(migrations)
1334
+ name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
1335
+ raise DuplicateMigrationNameError.new(name) if name
1336
+
1337
+ version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
1338
+ raise DuplicateMigrationVersionError.new(version) if version
1339
+ end
751
1340
 
752
- @migrated_versions ||= []
1341
+ def record_version_state_after_migrating(version)
753
1342
  if down?
754
- @migrated_versions.delete(version)
755
- stmt = table.where(table["version"].eq(version.to_s)).compile_delete
756
- Base.connection.delete stmt
1343
+ migrated.delete(version)
1344
+ @schema_migration.delete_by(version: version.to_s)
757
1345
  else
758
- @migrated_versions.push(version).sort!
759
- stmt = table.compile_insert table["version"] => version.to_s
760
- Base.connection.insert stmt
1346
+ migrated << version
1347
+ @schema_migration.create!(version: version.to_s)
761
1348
  end
762
1349
  end
763
1350
 
@@ -770,12 +1357,41 @@ module ActiveRecord
770
1357
  end
771
1358
 
772
1359
  # Wrap the migration in a transaction only if supported by the adapter.
773
- def ddl_transaction(&block)
774
- if Base.connection.supports_ddl_transactions?
775
- Base.transaction { block.call }
1360
+ def ddl_transaction(migration)
1361
+ if use_transaction?(migration)
1362
+ Base.transaction { yield }
776
1363
  else
777
- block.call
1364
+ yield
1365
+ end
1366
+ end
1367
+
1368
+ def use_transaction?(migration)
1369
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1370
+ end
1371
+
1372
+ def use_advisory_lock?
1373
+ Base.connection.advisory_locks_enabled?
1374
+ end
1375
+
1376
+ def with_advisory_lock
1377
+ lock_id = generate_migrator_advisory_lock_id
1378
+ connection = Base.connection
1379
+ got_lock = connection.get_advisory_lock(lock_id)
1380
+ raise ConcurrentMigrationError unless got_lock
1381
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1382
+ yield
1383
+ ensure
1384
+ if got_lock && !connection.release_advisory_lock(lock_id)
1385
+ raise ConcurrentMigrationError.new(
1386
+ ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
1387
+ )
778
1388
  end
779
1389
  end
1390
+
1391
+ MIGRATOR_SALT = 2053462845
1392
+ def generate_migrator_advisory_lock_id
1393
+ db_name_hash = Zlib.crc32(Base.connection.current_database)
1394
+ MIGRATOR_SALT * db_name_hash
1395
+ end
780
1396
  end
781
1397
  end