activerecord 3.2.22.5 → 5.2.8

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