activerecord 4.2.11.3 → 6.0.2.2

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

Potentially problematic release.


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

Files changed (372) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -1587
  3. data/MIT-LICENSE +4 -2
  4. data/README.rdoc +13 -12
  5. data/examples/performance.rb +33 -32
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record.rb +41 -22
  8. data/lib/active_record/aggregations.rb +267 -251
  9. data/lib/active_record/association_relation.rb +26 -12
  10. data/lib/active_record/associations.rb +1737 -1597
  11. data/lib/active_record/associations/alias_tracker.rb +29 -35
  12. data/lib/active_record/associations/association.rb +133 -58
  13. data/lib/active_record/associations/association_scope.rb +103 -132
  14. data/lib/active_record/associations/belongs_to_association.rb +65 -60
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -40
  17. data/lib/active_record/associations/builder/belongs_to.rb +69 -55
  18. data/lib/active_record/associations/builder/collection_association.rb +10 -33
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +52 -66
  20. data/lib/active_record/associations/builder/has_many.rb +8 -4
  21. data/lib/active_record/associations/builder/has_one.rb +46 -5
  22. data/lib/active_record/associations/builder/singular_association.rb +16 -10
  23. data/lib/active_record/associations/collection_association.rb +136 -288
  24. data/lib/active_record/associations/collection_proxy.rb +241 -146
  25. data/lib/active_record/associations/foreign_association.rb +10 -1
  26. data/lib/active_record/associations/has_many_association.rb +34 -97
  27. data/lib/active_record/associations/has_many_through_association.rb +60 -87
  28. data/lib/active_record/associations/has_one_association.rb +61 -49
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +141 -167
  31. data/lib/active_record/associations/join_dependency/join_association.rb +38 -86
  32. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  33. data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
  34. data/lib/active_record/associations/preloader.rb +90 -92
  35. data/lib/active_record/associations/preloader/association.rb +90 -123
  36. data/lib/active_record/associations/preloader/through_association.rb +85 -65
  37. data/lib/active_record/associations/singular_association.rb +18 -39
  38. data/lib/active_record/associations/through_association.rb +38 -18
  39. data/lib/active_record/attribute_assignment.rb +56 -183
  40. data/lib/active_record/attribute_decorators.rb +39 -15
  41. data/lib/active_record/attribute_methods.rb +120 -135
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -8
  43. data/lib/active_record/attribute_methods/dirty.rb +174 -144
  44. data/lib/active_record/attribute_methods/primary_key.rb +91 -83
  45. data/lib/active_record/attribute_methods/query.rb +6 -5
  46. data/lib/active_record/attribute_methods/read.rb +20 -76
  47. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
  49. data/lib/active_record/attribute_methods/write.rb +32 -54
  50. data/lib/active_record/attributes.rb +214 -82
  51. data/lib/active_record/autosave_association.rb +96 -38
  52. data/lib/active_record/base.rb +57 -45
  53. data/lib/active_record/callbacks.rb +100 -74
  54. data/lib/active_record/coders/json.rb +3 -1
  55. data/lib/active_record/coders/yaml_column.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +806 -296
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +26 -8
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +238 -115
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +83 -23
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +170 -53
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +74 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +356 -227
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +664 -243
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +191 -83
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +469 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +517 -633
  69. data/lib/active_record/connection_adapters/column.rb +56 -43
  70. data/lib/active_record/connection_adapters/connection_specification.rb +174 -152
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +29 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +202 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +72 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +95 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +88 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +31 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +58 -180
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +21 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +66 -114
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  96. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  98. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  100. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  102. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  103. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  105. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +10 -5
  106. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +144 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +470 -290
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +555 -356
  117. data/lib/active_record/connection_adapters/schema_cache.rb +72 -25
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +37 -0
  119. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +103 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +137 -0
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -345
  127. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  128. data/lib/active_record/connection_handling.rb +183 -41
  129. data/lib/active_record/core.rb +253 -229
  130. data/lib/active_record/counter_cache.rb +67 -49
  131. data/lib/active_record/database_configurations.rb +233 -0
  132. data/lib/active_record/database_configurations/database_config.rb +37 -0
  133. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  134. data/lib/active_record/database_configurations/url_config.rb +79 -0
  135. data/lib/active_record/define_callbacks.rb +22 -0
  136. data/lib/active_record/dynamic_matchers.rb +87 -105
  137. data/lib/active_record/enum.rb +163 -86
  138. data/lib/active_record/errors.rb +188 -53
  139. data/lib/active_record/explain.rb +23 -11
  140. data/lib/active_record/explain_registry.rb +4 -2
  141. data/lib/active_record/explain_subscriber.rb +10 -5
  142. data/lib/active_record/fixture_set/file.rb +35 -9
  143. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  144. data/lib/active_record/fixture_set/render_context.rb +17 -0
  145. data/lib/active_record/fixture_set/table_row.rb +153 -0
  146. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  147. data/lib/active_record/fixtures.rb +228 -499
  148. data/lib/active_record/gem_version.rb +6 -4
  149. data/lib/active_record/inheritance.rb +158 -112
  150. data/lib/active_record/insert_all.rb +179 -0
  151. data/lib/active_record/integration.rb +123 -29
  152. data/lib/active_record/internal_metadata.rb +53 -0
  153. data/lib/active_record/legacy_yaml_adapter.rb +21 -3
  154. data/lib/active_record/locale/en.yml +3 -2
  155. data/lib/active_record/locking/optimistic.rb +87 -96
  156. data/lib/active_record/locking/pessimistic.rb +18 -6
  157. data/lib/active_record/log_subscriber.rb +76 -33
  158. data/lib/active_record/middleware/database_selector.rb +75 -0
  159. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  160. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  161. data/lib/active_record/migration.rb +621 -303
  162. data/lib/active_record/migration/command_recorder.rb +177 -90
  163. data/lib/active_record/migration/compatibility.rb +244 -0
  164. data/lib/active_record/migration/join_table.rb +8 -6
  165. data/lib/active_record/model_schema.rb +315 -112
  166. data/lib/active_record/nested_attributes.rb +264 -222
  167. data/lib/active_record/no_touching.rb +14 -1
  168. data/lib/active_record/null_relation.rb +24 -37
  169. data/lib/active_record/persistence.rb +557 -125
  170. data/lib/active_record/query_cache.rb +19 -23
  171. data/lib/active_record/querying.rb +43 -29
  172. data/lib/active_record/railtie.rb +143 -44
  173. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  174. data/lib/active_record/railties/console_sandbox.rb +2 -0
  175. data/lib/active_record/railties/controller_runtime.rb +34 -33
  176. data/lib/active_record/railties/databases.rake +331 -185
  177. data/lib/active_record/readonly_attributes.rb +5 -4
  178. data/lib/active_record/reflection.rb +428 -279
  179. data/lib/active_record/relation.rb +519 -341
  180. data/lib/active_record/relation/batches.rb +207 -55
  181. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  182. data/lib/active_record/relation/calculations.rb +267 -253
  183. data/lib/active_record/relation/delegation.rb +70 -80
  184. data/lib/active_record/relation/finder_methods.rb +286 -241
  185. data/lib/active_record/relation/from_clause.rb +26 -0
  186. data/lib/active_record/relation/merger.rb +78 -87
  187. data/lib/active_record/relation/predicate_builder.rb +114 -119
  188. data/lib/active_record/relation/predicate_builder/array_handler.rb +27 -26
  189. data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
  190. data/lib/active_record/relation/predicate_builder/base_handler.rb +18 -0
  191. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
  193. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  194. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  195. data/lib/active_record/relation/query_attribute.rb +50 -0
  196. data/lib/active_record/relation/query_methods.rb +597 -393
  197. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  198. data/lib/active_record/relation/spawn_methods.rb +11 -13
  199. data/lib/active_record/relation/where_clause.rb +190 -0
  200. data/lib/active_record/relation/where_clause_factory.rb +33 -0
  201. data/lib/active_record/result.rb +79 -42
  202. data/lib/active_record/runtime_registry.rb +6 -4
  203. data/lib/active_record/sanitization.rb +144 -121
  204. data/lib/active_record/schema.rb +21 -24
  205. data/lib/active_record/schema_dumper.rb +112 -93
  206. data/lib/active_record/schema_migration.rb +24 -17
  207. data/lib/active_record/scoping.rb +45 -26
  208. data/lib/active_record/scoping/default.rb +101 -85
  209. data/lib/active_record/scoping/named.rb +87 -33
  210. data/lib/active_record/secure_token.rb +40 -0
  211. data/lib/active_record/serialization.rb +5 -5
  212. data/lib/active_record/statement_cache.rb +73 -36
  213. data/lib/active_record/store.rb +127 -42
  214. data/lib/active_record/suppressor.rb +61 -0
  215. data/lib/active_record/table_metadata.rb +75 -0
  216. data/lib/active_record/tasks/database_tasks.rb +307 -100
  217. data/lib/active_record/tasks/mysql_database_tasks.rb +55 -99
  218. data/lib/active_record/tasks/postgresql_database_tasks.rb +81 -41
  219. data/lib/active_record/tasks/sqlite_database_tasks.rb +38 -16
  220. data/lib/active_record/test_databases.rb +23 -0
  221. data/lib/active_record/test_fixtures.rb +225 -0
  222. data/lib/active_record/timestamp.rb +86 -40
  223. data/lib/active_record/touch_later.rb +66 -0
  224. data/lib/active_record/transactions.rb +217 -151
  225. data/lib/active_record/translation.rb +3 -1
  226. data/lib/active_record/type.rb +78 -23
  227. data/lib/active_record/type/adapter_specific_registry.rb +129 -0
  228. data/lib/active_record/type/date.rb +4 -45
  229. data/lib/active_record/type/date_time.rb +4 -49
  230. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  231. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  232. data/lib/active_record/type/internal/timezone.rb +17 -0
  233. data/lib/active_record/type/json.rb +30 -0
  234. data/lib/active_record/type/serialized.rb +24 -15
  235. data/lib/active_record/type/text.rb +2 -2
  236. data/lib/active_record/type/time.rb +11 -16
  237. data/lib/active_record/type/type_map.rb +15 -17
  238. data/lib/active_record/type/unsigned_integer.rb +9 -7
  239. data/lib/active_record/type_caster.rb +9 -0
  240. data/lib/active_record/type_caster/connection.rb +34 -0
  241. data/lib/active_record/type_caster/map.rb +20 -0
  242. data/lib/active_record/validations.rb +39 -35
  243. data/lib/active_record/validations/absence.rb +25 -0
  244. data/lib/active_record/validations/associated.rb +13 -4
  245. data/lib/active_record/validations/length.rb +26 -0
  246. data/lib/active_record/validations/presence.rb +14 -13
  247. data/lib/active_record/validations/uniqueness.rb +42 -55
  248. data/lib/active_record/version.rb +3 -1
  249. data/lib/arel.rb +58 -0
  250. data/lib/arel/alias_predication.rb +9 -0
  251. data/lib/arel/attributes.rb +22 -0
  252. data/lib/arel/attributes/attribute.rb +37 -0
  253. data/lib/arel/collectors/bind.rb +24 -0
  254. data/lib/arel/collectors/composite.rb +31 -0
  255. data/lib/arel/collectors/plain_string.rb +20 -0
  256. data/lib/arel/collectors/sql_string.rb +20 -0
  257. data/lib/arel/collectors/substitute_binds.rb +28 -0
  258. data/lib/arel/crud.rb +42 -0
  259. data/lib/arel/delete_manager.rb +18 -0
  260. data/lib/arel/errors.rb +9 -0
  261. data/lib/arel/expressions.rb +29 -0
  262. data/lib/arel/factory_methods.rb +49 -0
  263. data/lib/arel/insert_manager.rb +49 -0
  264. data/lib/arel/math.rb +45 -0
  265. data/lib/arel/nodes.rb +68 -0
  266. data/lib/arel/nodes/and.rb +32 -0
  267. data/lib/arel/nodes/ascending.rb +23 -0
  268. data/lib/arel/nodes/binary.rb +52 -0
  269. data/lib/arel/nodes/bind_param.rb +36 -0
  270. data/lib/arel/nodes/case.rb +55 -0
  271. data/lib/arel/nodes/casted.rb +50 -0
  272. data/lib/arel/nodes/comment.rb +29 -0
  273. data/lib/arel/nodes/count.rb +12 -0
  274. data/lib/arel/nodes/delete_statement.rb +45 -0
  275. data/lib/arel/nodes/descending.rb +23 -0
  276. data/lib/arel/nodes/equality.rb +18 -0
  277. data/lib/arel/nodes/extract.rb +24 -0
  278. data/lib/arel/nodes/false.rb +16 -0
  279. data/lib/arel/nodes/full_outer_join.rb +8 -0
  280. data/lib/arel/nodes/function.rb +44 -0
  281. data/lib/arel/nodes/grouping.rb +8 -0
  282. data/lib/arel/nodes/in.rb +8 -0
  283. data/lib/arel/nodes/infix_operation.rb +80 -0
  284. data/lib/arel/nodes/inner_join.rb +8 -0
  285. data/lib/arel/nodes/insert_statement.rb +37 -0
  286. data/lib/arel/nodes/join_source.rb +20 -0
  287. data/lib/arel/nodes/matches.rb +18 -0
  288. data/lib/arel/nodes/named_function.rb +23 -0
  289. data/lib/arel/nodes/node.rb +50 -0
  290. data/lib/arel/nodes/node_expression.rb +13 -0
  291. data/lib/arel/nodes/outer_join.rb +8 -0
  292. data/lib/arel/nodes/over.rb +15 -0
  293. data/lib/arel/nodes/regexp.rb +16 -0
  294. data/lib/arel/nodes/right_outer_join.rb +8 -0
  295. data/lib/arel/nodes/select_core.rb +67 -0
  296. data/lib/arel/nodes/select_statement.rb +41 -0
  297. data/lib/arel/nodes/sql_literal.rb +16 -0
  298. data/lib/arel/nodes/string_join.rb +11 -0
  299. data/lib/arel/nodes/table_alias.rb +27 -0
  300. data/lib/arel/nodes/terminal.rb +16 -0
  301. data/lib/arel/nodes/true.rb +16 -0
  302. data/lib/arel/nodes/unary.rb +45 -0
  303. data/lib/arel/nodes/unary_operation.rb +20 -0
  304. data/lib/arel/nodes/unqualified_column.rb +22 -0
  305. data/lib/arel/nodes/update_statement.rb +41 -0
  306. data/lib/arel/nodes/values_list.rb +9 -0
  307. data/lib/arel/nodes/window.rb +126 -0
  308. data/lib/arel/nodes/with.rb +11 -0
  309. data/lib/arel/order_predications.rb +13 -0
  310. data/lib/arel/predications.rb +257 -0
  311. data/lib/arel/select_manager.rb +271 -0
  312. data/lib/arel/table.rb +110 -0
  313. data/lib/arel/tree_manager.rb +72 -0
  314. data/lib/arel/update_manager.rb +34 -0
  315. data/lib/arel/visitors.rb +20 -0
  316. data/lib/arel/visitors/depth_first.rb +204 -0
  317. data/lib/arel/visitors/dot.rb +297 -0
  318. data/lib/arel/visitors/ibm_db.rb +34 -0
  319. data/lib/arel/visitors/informix.rb +62 -0
  320. data/lib/arel/visitors/mssql.rb +157 -0
  321. data/lib/arel/visitors/mysql.rb +83 -0
  322. data/lib/arel/visitors/oracle.rb +159 -0
  323. data/lib/arel/visitors/oracle12.rb +66 -0
  324. data/lib/arel/visitors/postgresql.rb +110 -0
  325. data/lib/arel/visitors/sqlite.rb +39 -0
  326. data/lib/arel/visitors/to_sql.rb +889 -0
  327. data/lib/arel/visitors/visitor.rb +46 -0
  328. data/lib/arel/visitors/where_sql.rb +23 -0
  329. data/lib/arel/window_predications.rb +9 -0
  330. data/lib/rails/generators/active_record.rb +7 -5
  331. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  332. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  333. data/lib/rails/generators/active_record/migration.rb +31 -1
  334. data/lib/rails/generators/active_record/migration/migration_generator.rb +42 -37
  335. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  336. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +11 -2
  337. data/lib/rails/generators/active_record/model/model_generator.rb +19 -22
  338. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  339. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  340. metadata +166 -58
  341. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  342. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  343. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  344. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  345. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  346. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  347. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  348. data/lib/active_record/attribute.rb +0 -163
  349. data/lib/active_record/attribute_set.rb +0 -81
  350. data/lib/active_record/attribute_set/builder.rb +0 -106
  351. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
  352. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  353. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  354. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  355. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  356. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  359. data/lib/active_record/type/big_integer.rb +0 -13
  360. data/lib/active_record/type/binary.rb +0 -50
  361. data/lib/active_record/type/boolean.rb +0 -31
  362. data/lib/active_record/type/decimal.rb +0 -64
  363. data/lib/active_record/type/decorator.rb +0 -14
  364. data/lib/active_record/type/float.rb +0 -19
  365. data/lib/active_record/type/integer.rb +0 -59
  366. data/lib/active_record/type/mutable.rb +0 -16
  367. data/lib/active_record/type/numeric.rb +0 -36
  368. data/lib/active_record/type/string.rb +0 -40
  369. data/lib/active_record/type/time_value.rb +0 -38
  370. data/lib/active_record/type/value.rb +0 -110
  371. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
  372. data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Locking
3
5
  # Locking::Pessimistic provides support for row-level locking using
@@ -12,9 +14,9 @@ module ActiveRecord
12
14
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
13
15
  #
14
16
  # Account.transaction do
15
- # # select * from accounts where name = 'shugo' limit 1 for update
16
- # shugo = Account.where("name = 'shugo'").lock(true).first
17
- # yuko = Account.where("name = 'yuko'").lock(true).first
17
+ # # select * from accounts where name = 'shugo' limit 1 for update nowait
18
+ # shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
19
+ # yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
18
20
  # shugo.balance -= 100
19
21
  # shugo.save!
20
22
  # yuko.balance += 100
@@ -51,15 +53,25 @@ module ActiveRecord
51
53
  # end
52
54
  #
53
55
  # Database-specific information on row locking:
54
- # MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
55
- # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ # MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
57
+ # PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
58
  module Pessimistic
57
59
  # Obtain a row lock on this record. Reloads the record to obtain the requested
58
60
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
61
  # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
62
  # the locked record.
61
63
  def lock!(lock = true)
62
- reload(:lock => lock) if persisted?
64
+ if persisted?
65
+ if has_changes_to_save?
66
+ raise(<<-MSG.squish)
67
+ Locking a record with unpersisted changes is not supported. Use
68
+ `save` to persist the changes, or `reload` to discard them
69
+ explicitly.
70
+ MSG
71
+ end
72
+
73
+ reload(lock: lock)
74
+ end
63
75
  self
64
76
  end
65
77
 
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  class LogSubscriber < ActiveSupport::LogSubscriber
3
5
  IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
4
6
 
7
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
+
5
9
  def self.runtime=(value)
6
10
  ActiveRecord::RuntimeRegistry.sql_runtime = value
7
11
  end
@@ -15,25 +19,6 @@ module ActiveRecord
15
19
  rt
16
20
  end
17
21
 
18
- def initialize
19
- super
20
- @odd = false
21
- end
22
-
23
- def render_bind(column, value)
24
- if column
25
- if column.binary?
26
- # This specifically deals with the PG adapter that casts bytea columns into a Hash.
27
- value = value[:value] if value.is_a?(Hash)
28
- value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
29
- end
30
-
31
- [column.name, value]
32
- else
33
- [nil, value]
34
- end
35
- end
36
-
37
22
  def sql(event)
38
23
  self.class.runtime += event.duration
39
24
  return unless logger.debug?
@@ -43,32 +28,90 @@ module ActiveRecord
43
28
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
44
29
 
45
30
  name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
31
+ name = "CACHE #{name}" if payload[:cached]
46
32
  sql = payload[:sql]
47
33
  binds = nil
48
34
 
49
35
  unless (payload[:binds] || []).empty?
50
- binds = " " + payload[:binds].map { |col,v|
51
- render_bind(col, v)
36
+ casted_params = type_casted_binds(payload[:type_casted_binds])
37
+ binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
+ render_bind(attr, value)
52
39
  }.inspect
53
40
  end
54
41
 
55
- if odd?
56
- name = color(name, CYAN, true)
57
- sql = color(sql, nil, true)
58
- else
59
- name = color(name, MAGENTA, true)
60
- end
42
+ name = colorize_payload_name(name, payload[:name])
43
+ sql = color(sql, sql_color(sql), true)
61
44
 
62
45
  debug " #{name} #{sql}#{binds}"
63
46
  end
64
47
 
65
- def odd?
66
- @odd = !@odd
67
- end
48
+ private
49
+ def type_casted_binds(casted_binds)
50
+ casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
51
+ end
68
52
 
69
- def logger
70
- ActiveRecord::Base.logger
71
- end
53
+ def render_bind(attr, value)
54
+ if attr.is_a?(Array)
55
+ attr = attr.first
56
+ elsif attr.type.binary? && attr.value
57
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
58
+ end
59
+
60
+ [attr && attr.name, value]
61
+ end
62
+
63
+ def colorize_payload_name(name, payload_name)
64
+ if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
65
+ color(name, MAGENTA, true)
66
+ else
67
+ color(name, CYAN, true)
68
+ end
69
+ end
70
+
71
+ def sql_color(sql)
72
+ case sql
73
+ when /\A\s*rollback/mi
74
+ RED
75
+ when /select .*for update/mi, /\A\s*lock/mi
76
+ WHITE
77
+ when /\A\s*select/i
78
+ BLUE
79
+ when /\A\s*insert/i
80
+ GREEN
81
+ when /\A\s*update/i
82
+ YELLOW
83
+ when /\A\s*delete/i
84
+ RED
85
+ when /transaction\s*\Z/i
86
+ CYAN
87
+ else
88
+ MAGENTA
89
+ end
90
+ end
91
+
92
+ def logger
93
+ ActiveRecord::Base.logger
94
+ end
95
+
96
+ def debug(progname = nil, &block)
97
+ return unless super
98
+
99
+ if ActiveRecord::Base.verbose_query_logs
100
+ log_query_source
101
+ end
102
+ end
103
+
104
+ def log_query_source
105
+ source = extract_query_source_location(caller)
106
+
107
+ if source
108
+ logger.debug(" ↳ #{source}")
109
+ end
110
+ end
111
+
112
+ def extract_query_source_location(locations)
113
+ backtrace_cleaner.clean(locations.lazy).first
114
+ end
72
115
  end
73
116
  end
74
117
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ # The DatabaseSelector Middleware provides a framework for automatically
8
+ # swapping from the primary to the replica database connection. Rails
9
+ # provides a basic framework to determine when to swap and allows for
10
+ # applications to write custom strategy classes to override the default
11
+ # behavior.
12
+ #
13
+ # The resolver class defines when the application should switch (i.e. read
14
+ # from the primary if a write occurred less than 2 seconds ago) and a
15
+ # resolver context class that sets a value that helps the resolver class
16
+ # decide when to switch.
17
+ #
18
+ # Rails default middleware uses the request's session to set a timestamp
19
+ # that informs the application when to read from a primary or read from a
20
+ # replica.
21
+ #
22
+ # To use the DatabaseSelector in your application with default settings add
23
+ # the following options to your environment config:
24
+ #
25
+ # config.active_record.database_selector = { delay: 2.seconds }
26
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
27
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
28
+ #
29
+ # New applications will include these lines commented out in the production.rb.
30
+ #
31
+ # The default behavior can be changed by setting the config options to a
32
+ # custom class:
33
+ #
34
+ # config.active_record.database_selector = { delay: 2.seconds }
35
+ # config.active_record.database_resolver = MyResolver
36
+ # config.active_record.database_resolver_context = MyResolver::MySession
37
+ class DatabaseSelector
38
+ def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
39
+ @app = app
40
+ @resolver_klass = resolver_klass || Resolver
41
+ @context_klass = context_klass || Resolver::Session
42
+ @options = options
43
+ end
44
+
45
+ attr_reader :resolver_klass, :context_klass, :options
46
+
47
+ # Middleware that determines which database connection to use in a multiple
48
+ # database application.
49
+ def call(env)
50
+ request = ActionDispatch::Request.new(env)
51
+
52
+ select_database(request) do
53
+ @app.call(env)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def select_database(request, &blk)
60
+ context = context_klass.call(request)
61
+ resolver = resolver_klass.call(context, options)
62
+
63
+ if reading_request?(request)
64
+ resolver.read(&blk)
65
+ else
66
+ resolver.write(&blk)
67
+ end
68
+ end
69
+
70
+ def reading_request?(request)
71
+ request.get? || request.head?
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver/session"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ class DatabaseSelector
8
+ # The Resolver class is used by the DatabaseSelector middleware to
9
+ # determine which database the request should use.
10
+ #
11
+ # To change the behavior of the Resolver class in your application,
12
+ # create a custom resolver class that inherits from
13
+ # DatabaseSelector::Resolver and implements the methods that need to
14
+ # be changed.
15
+ #
16
+ # By default the Resolver class will send read traffic to the replica
17
+ # if it's been 2 seconds since the last write.
18
+ class Resolver # :nodoc:
19
+ SEND_TO_REPLICA_DELAY = 2.seconds
20
+
21
+ def self.call(context, options = {})
22
+ new(context, options)
23
+ end
24
+
25
+ def initialize(context, options = {})
26
+ @context = context
27
+ @options = options
28
+ @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
29
+ @instrumenter = ActiveSupport::Notifications.instrumenter
30
+ end
31
+
32
+ attr_reader :context, :delay, :instrumenter
33
+
34
+ def read(&blk)
35
+ if read_from_primary?
36
+ read_from_primary(&blk)
37
+ else
38
+ read_from_replica(&blk)
39
+ end
40
+ end
41
+
42
+ def write(&blk)
43
+ write_to_primary(&blk)
44
+ end
45
+
46
+ private
47
+
48
+ def read_from_primary(&blk)
49
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
50
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
51
+ yield
52
+ end
53
+ end
54
+ end
55
+
56
+ def read_from_replica(&blk)
57
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
58
+ instrumenter.instrument("database_selector.active_record.read_from_replica") do
59
+ yield
60
+ end
61
+ end
62
+ end
63
+
64
+ def write_to_primary(&blk)
65
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
66
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
67
+ yield
68
+ ensure
69
+ context.update_last_write_timestamp
70
+ end
71
+ end
72
+ end
73
+
74
+ def read_from_primary?
75
+ !time_since_last_write_ok?
76
+ end
77
+
78
+ def send_to_replica_delay
79
+ delay
80
+ end
81
+
82
+ def time_since_last_write_ok?
83
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Middleware
5
+ class DatabaseSelector
6
+ class Resolver
7
+ # The session class is used by the DatabaseSelector::Resolver to save
8
+ # timestamps of the last write in the session.
9
+ #
10
+ # The last_write is used to determine whether it's safe to read
11
+ # from the replica or the request needs to be sent to the primary.
12
+ class Session # :nodoc:
13
+ def self.call(request)
14
+ new(request.session)
15
+ end
16
+
17
+ # Converts time to a timestamp that represents milliseconds since
18
+ # epoch.
19
+ def self.convert_time_to_timestamp(time)
20
+ time.to_i * 1000 + time.usec / 1000
21
+ end
22
+
23
+ # Converts milliseconds since epoch timestamp into a time object.
24
+ def self.convert_timestamp_to_time(timestamp)
25
+ timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
26
+ end
27
+
28
+ def initialize(session)
29
+ @session = session
30
+ end
31
+
32
+ attr_reader :session
33
+
34
+ def last_write_timestamp
35
+ self.class.convert_timestamp_to_time(session[:last_write])
36
+ end
37
+
38
+ def update_last_write_timestamp
39
+ session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,48 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "set"
5
+ require "zlib"
1
6
  require "active_support/core_ext/module/attribute_accessors"
2
- require 'set'
7
+ require "active_support/actionable_error"
3
8
 
4
9
  module ActiveRecord
5
- class MigrationError < ActiveRecordError#:nodoc:
10
+ class MigrationError < ActiveRecordError #:nodoc:
6
11
  def initialize(message = nil)
7
12
  message = "\n\n#{message}\n\n" if message
8
13
  super
9
14
  end
10
15
  end
11
16
 
12
- # Exception that can be raised to stop migrations from going backwards.
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
13
88
  class IrreversibleMigration < MigrationError
14
89
  end
15
90
 
16
- class DuplicateMigrationVersionError < MigrationError#:nodoc:
17
- def initialize(version)
18
- super("Multiple migrations have the version number #{version}")
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
19
98
  end
20
99
  end
21
100
 
22
- class DuplicateMigrationNameError < MigrationError#:nodoc:
23
- def initialize(name)
24
- super("Multiple migrations have the name #{name}")
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
25
108
  end
26
109
  end
27
110
 
28
111
  class UnknownMigrationVersionError < MigrationError #:nodoc:
29
- def initialize(version)
30
- super("No migration with version number #{version}")
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
31
118
  end
32
119
  end
33
120
 
34
- class IllegalMigrationNameError < MigrationError#:nodoc:
35
- def initialize(name)
36
- super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
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
37
128
  end
38
129
  end
39
130
 
40
- class PendingMigrationError < MigrationError#:nodoc:
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
146
+ end
147
+ end
148
+
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:
41
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
166
+ end
167
+ end
168
+
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)
175
+ end
176
+ end
177
+
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"
42
184
  if defined?(Rails.env)
43
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
185
+ super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
44
186
  else
45
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
187
+ super("#{msg}\n\n")
46
188
  end
47
189
  end
48
190
  end
@@ -59,7 +201,7 @@ module ActiveRecord
59
201
  #
60
202
  # Example of a simple migration:
61
203
  #
62
- # class AddSsl < ActiveRecord::Migration
204
+ # class AddSsl < ActiveRecord::Migration[5.0]
63
205
  # def up
64
206
  # add_column :accounts, :ssl_enabled, :boolean, default: true
65
207
  # end
@@ -79,7 +221,7 @@ module ActiveRecord
79
221
  #
80
222
  # Example of a more complex migration that also needs to initialize data:
81
223
  #
82
- # class AddSystemSettings < ActiveRecord::Migration
224
+ # class AddSystemSettings < ActiveRecord::Migration[5.0]
83
225
  # def up
84
226
  # create_table :system_settings do |t|
85
227
  # t.string :name
@@ -106,17 +248,18 @@ module ActiveRecord
106
248
  #
107
249
  # == Available transformations
108
250
  #
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.
109
258
  # * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
110
259
  # makes the table object available to a block that can then add columns to it,
111
260
  # following the same format as +add_column+. See example above. The options hash
112
261
  # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
113
262
  # table definition.
114
- # * <tt>drop_table(name)</tt>: Drops the table called +name+.
115
- # * <tt>change_table(name, options)</tt>: Allows to make column alterations to
116
- # the table called +name+. It makes the table object available to a block that
117
- # can then add/remove columns, indexes or foreign keys to it.
118
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
119
- # to +new_name+.
120
263
  # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
121
264
  # to the table called +table_name+
122
265
  # named +column_name+ specified to be one of the following types:
@@ -127,21 +270,61 @@ module ActiveRecord
127
270
  # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
128
271
  # <tt>{ limit: 50, null: false }</tt>) -- see
129
272
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
130
- # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
131
- # a column but keeps the type and content.
132
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
133
- # the column to a different type using the same parameters as add_column.
134
- # * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
135
- # named +column_name+ 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.
136
276
  # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
137
277
  # with the name of the column. Other options include
138
278
  # <tt>:name</tt>, <tt>:unique</tt> (e.g.
139
279
  # <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
140
280
  # (e.g. <tt>{ order: { name: :desc } }</tt>).
141
- # * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
142
- # specified by +column_name+.
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+.
143
322
  # * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
144
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.
145
328
  #
146
329
  # == Irreversible transformations
147
330
  #
@@ -165,24 +348,24 @@ module ActiveRecord
165
348
  #
166
349
  # rails generate migration add_fieldname_to_tablename fieldname:string
167
350
  #
168
- # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
169
- # class AddFieldnameToTablename < ActiveRecord::Migration
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]
170
353
  # def change
171
- # add_column :tablenames, :field, :string
354
+ # add_column :tablenames, :fieldname, :string
172
355
  # end
173
356
  # end
174
357
  #
175
358
  # To run migrations against the currently configured database, use
176
- # <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
177
360
  # pending migrations, creating the <tt>schema_migrations</tt> table
178
361
  # (see "About the schema_migrations table" section below) if missing. It will also
179
- # 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
180
363
  # to match the structure of your database.
181
364
  #
182
365
  # To roll the database back to a previous migration version, use
183
- # <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
366
+ # <tt>rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
184
367
  # you wish to downgrade. Alternatively, you can also use the STEP option if you
185
- # wish to rollback last few migrations. <tt>rake db:migrate STEP=2</tt> will rollback
368
+ # wish to rollback last few migrations. <tt>rails db:rollback STEP=2</tt> will rollback
186
369
  # the latest two migrations.
187
370
  #
188
371
  # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
@@ -197,7 +380,7 @@ module ActiveRecord
197
380
  #
198
381
  # Not all migrations change the schema. Some just fix the data:
199
382
  #
200
- # class RemoveEmptyTags < ActiveRecord::Migration
383
+ # class RemoveEmptyTags < ActiveRecord::Migration[5.0]
201
384
  # def up
202
385
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
203
386
  # end
@@ -210,7 +393,7 @@ module ActiveRecord
210
393
  #
211
394
  # Others remove columns when they migrate up instead of down:
212
395
  #
213
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
396
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
214
397
  # def up
215
398
  # remove_column :items, :incomplete_items_count
216
399
  # remove_column :items, :completed_items_count
@@ -224,7 +407,7 @@ module ActiveRecord
224
407
  #
225
408
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
226
409
  #
227
- # class MakeJoinUnique < ActiveRecord::Migration
410
+ # class MakeJoinUnique < ActiveRecord::Migration[5.0]
228
411
  # def up
229
412
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
230
413
  # end
@@ -241,7 +424,7 @@ module ActiveRecord
241
424
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
242
425
  # latest column data from after the new column was added. Example:
243
426
  #
244
- # class AddPeopleSalary < ActiveRecord::Migration
427
+ # class AddPeopleSalary < ActiveRecord::Migration[5.0]
245
428
  # def up
246
429
  # add_column :people, :salary, :integer
247
430
  # Person.reset_column_information
@@ -275,21 +458,6 @@ module ActiveRecord
275
458
  # The phrase "Updating salaries..." would then be printed, along with the
276
459
  # benchmark for the block when the block completes.
277
460
  #
278
- # == About the schema_migrations table
279
- #
280
- # Rails versions 2.0 and prior used to create a table called
281
- # <tt>schema_info</tt> when using migrations. This table contained the
282
- # version of the schema as of the last applied migration.
283
- #
284
- # Starting with Rails 2.1, the <tt>schema_info</tt> table is
285
- # (automatically) replaced by the <tt>schema_migrations</tt> table, which
286
- # contains the version numbers of all the migrations applied.
287
- #
288
- # As a result, it is now possible to add migration files that are numbered
289
- # lower than the current schema version: when migrating up, those
290
- # never-applied "interleaved" migrations will be automatically applied, and
291
- # when migrating down, never-applied "interleaved" migrations will be skipped.
292
- #
293
461
  # == Timestamped Migrations
294
462
  #
295
463
  # By default, Rails generates migrations that look like:
@@ -314,7 +482,7 @@ module ActiveRecord
314
482
  # To define a reversible migration, define the +change+ method in your
315
483
  # migration like this:
316
484
  #
317
- # class TenderloveMigration < ActiveRecord::Migration
485
+ # class TenderloveMigration < ActiveRecord::Migration[5.0]
318
486
  # def change
319
487
  # create_table(:horses) do |t|
320
488
  # t.column :content, :text
@@ -326,9 +494,9 @@ module ActiveRecord
326
494
  # This migration will create the horses table for you on the way up, and
327
495
  # automatically figure out how to drop the table on the way down.
328
496
  #
329
- # Some commands like +remove_column+ cannot be reversed. If you care to
330
- # define how to move up and down in these cases, you should define the +up+
331
- # 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.
332
500
  #
333
501
  # If a command cannot be reversed, an
334
502
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -344,7 +512,7 @@ module ActiveRecord
344
512
  # can't execute inside a transaction though, and for these situations
345
513
  # you can turn the automatic transactions off.
346
514
  #
347
- # class ChangeEnum < ActiveRecord::Migration
515
+ # class ChangeEnum < ActiveRecord::Migration[5.0]
348
516
  # disable_ddl_transaction!
349
517
  #
350
518
  # def up
@@ -355,11 +523,35 @@ module ActiveRecord
355
523
  # Remember that you can still open your own transactions, even if you
356
524
  # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
357
525
  class Migration
358
- autoload :CommandRecorder, 'active_record/migration/command_recorder'
526
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
527
+ autoload :Compatibility, "active_record/migration/compatibility"
359
528
 
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
541
+ end
542
+
543
+ def self.[](version)
544
+ Compatibility.find(version)
545
+ end
546
+
547
+ def self.current_version
548
+ ActiveRecord::VERSION::STRING.to_f
549
+ end
550
+
551
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
360
552
 
361
553
  # This class is used to verify that all migrations have been run before
362
- # loading a web page if config.active_record.migration_error is set to :page_load
554
+ # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
363
555
  class CheckPending
364
556
  def initialize(app)
365
557
  @app = app
@@ -367,53 +559,65 @@ module ActiveRecord
367
559
  end
368
560
 
369
561
  def call(env)
370
- if connection.supports_migrations?
371
- mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
372
- if @last_check < mtime
373
- ActiveRecord::Migration.check_pending!(connection)
374
- @last_check = mtime
375
- end
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
376
566
  end
377
567
  @app.call(env)
378
568
  end
379
569
 
380
570
  private
381
571
 
382
- def connection
383
- ActiveRecord::Base.connection
384
- end
572
+ def connection
573
+ ActiveRecord::Base.connection
574
+ end
385
575
  end
386
576
 
387
577
  class << self
388
- attr_accessor :delegate # :nodoc:
389
- attr_accessor :disable_ddl_transaction # :nodoc:
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
390
584
 
585
+ # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
391
586
  def check_pending!(connection = Base.connection)
392
- raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
587
+ raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
393
588
  end
394
589
 
395
590
  def load_schema_if_pending!
396
- if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
397
- # Roundrip to Rake to allow plugins to hook into database initialization.
398
- FileUtils.cd Rails.root do
399
- current_config = Base.connection_config
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
400
602
  Base.clear_all_connections!
401
- system("bin/rake db:test:prepare")
402
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
403
- Base.establish_connection(current_config)
603
+ system("bin/rails db:test:prepare")
404
604
  end
405
- check_pending!
406
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!
407
611
  end
408
612
 
409
- def maintain_test_schema! # :nodoc:
613
+ def maintain_test_schema! #:nodoc:
410
614
  if ActiveRecord::Base.maintain_test_schema
411
615
  suppress_messages { load_schema_if_pending! }
412
616
  end
413
617
  end
414
618
 
415
- def method_missing(name, *args, &block) # :nodoc:
416
- (delegate || superclass.delegate).send(name, *args, &block)
619
+ def method_missing(name, *args, &block) #:nodoc:
620
+ nearest_delegate.send(name, *args, &block)
417
621
  end
418
622
 
419
623
  def migrate(direction)
@@ -429,7 +633,7 @@ module ActiveRecord
429
633
  end
430
634
  end
431
635
 
432
- def disable_ddl_transaction # :nodoc:
636
+ def disable_ddl_transaction #:nodoc:
433
637
  self.class.disable_ddl_transaction
434
638
  end
435
639
 
@@ -453,7 +657,7 @@ module ActiveRecord
453
657
  # and create the table 'apples' on the way up, and the reverse
454
658
  # on the way down.
455
659
  #
456
- # class FixTLMigration < ActiveRecord::Migration
660
+ # class FixTLMigration < ActiveRecord::Migration[5.0]
457
661
  # def change
458
662
  # revert do
459
663
  # create_table(:horses) do |t|
@@ -470,9 +674,9 @@ module ActiveRecord
470
674
  # Or equivalently, if +TenderloveMigration+ is defined as in the
471
675
  # documentation for Migration:
472
676
  #
473
- # require_relative '2012121212_tenderlove_migration'
677
+ # require_relative '20121212123456_tenderlove_migration'
474
678
  #
475
- # class FixupTLMigration < ActiveRecord::Migration
679
+ # class FixupTLMigration < ActiveRecord::Migration[5.0]
476
680
  # def change
477
681
  # revert TenderloveMigration
478
682
  #
@@ -486,27 +690,25 @@ module ActiveRecord
486
690
  def revert(*migration_classes)
487
691
  run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
488
692
  if block_given?
489
- if @connection.respond_to? :revert
490
- @connection.revert { yield }
693
+ if connection.respond_to? :revert
694
+ connection.revert { yield }
491
695
  else
492
- recorder = CommandRecorder.new(@connection)
696
+ recorder = command_recorder
493
697
  @connection = recorder
494
698
  suppress_messages do
495
- @connection.revert { yield }
699
+ connection.revert { yield }
496
700
  end
497
701
  @connection = recorder.delegate
498
- recorder.commands.each do |cmd, args, block|
499
- send(cmd, *args, &block)
500
- end
702
+ recorder.replay(self)
501
703
  end
502
704
  end
503
705
  end
504
706
 
505
707
  def reverting?
506
- @connection.respond_to?(:reverting) && @connection.reverting
708
+ connection.respond_to?(:reverting) && connection.reverting
507
709
  end
508
710
 
509
- class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
510
712
  def up
511
713
  yield unless reverting
512
714
  end
@@ -525,7 +727,7 @@ module ActiveRecord
525
727
  # when the three columns 'first_name', 'last_name' and 'full_name' exist,
526
728
  # even when migrating down:
527
729
  #
528
- # class SplitNameMigration < ActiveRecord::Migration
730
+ # class SplitNameMigration < ActiveRecord::Migration[5.0]
529
731
  # def change
530
732
  # add_column :users, :first_name, :string
531
733
  # add_column :users, :last_name, :string
@@ -544,7 +746,25 @@ module ActiveRecord
544
746
  # end
545
747
  def reversible
546
748
  helper = ReversibleBlockHelper.new(reverting?)
547
- execute_block{ yield helper }
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?
548
768
  end
549
769
 
550
770
  # Runs the given migration classes.
@@ -560,7 +780,7 @@ module ActiveRecord
560
780
  revert { run(*migration_classes, direction: dir, revert: true) }
561
781
  else
562
782
  migration_classes.each do |migration_class|
563
- migration_class.new.exec_migration(@connection, dir)
783
+ migration_class.new.exec_migration(connection, dir)
564
784
  end
565
785
  end
566
786
  end
@@ -586,7 +806,7 @@ module ActiveRecord
586
806
  when :down then announce "reverting"
587
807
  end
588
808
 
589
- time = nil
809
+ time = nil
590
810
  ActiveRecord::Base.connection_pool.with_connection do |conn|
591
811
  time = Benchmark.measure do
592
812
  exec_migration(conn, direction)
@@ -614,7 +834,7 @@ module ActiveRecord
614
834
  @connection = nil
615
835
  end
616
836
 
617
- def write(text="")
837
+ def write(text = "")
618
838
  puts(text) if verbose
619
839
  end
620
840
 
@@ -624,10 +844,14 @@ module ActiveRecord
624
844
  write "== %s %s" % [text, "=" * length]
625
845
  end
626
846
 
627
- 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)
628
850
  write "#{subitem ? " ->" : "--"} #{message}"
629
851
  end
630
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.
631
855
  def say_with_time(message)
632
856
  say(message)
633
857
  result = nil
@@ -637,6 +861,7 @@ module ActiveRecord
637
861
  result
638
862
  end
639
863
 
864
+ # Takes a block as an argument and suppresses any output generated by the block.
640
865
  def suppress_messages
641
866
  save, self.verbose = verbose, false
642
867
  yield
@@ -649,10 +874,10 @@ module ActiveRecord
649
874
  end
650
875
 
651
876
  def method_missing(method, *arguments, &block)
652
- arg_list = arguments.map{ |a| a.inspect } * ', '
877
+ arg_list = arguments.map(&:inspect) * ", "
653
878
 
654
879
  say_with_time "#{method}(#{arg_list})" do
655
- unless @connection.respond_to? :revert
880
+ unless connection.respond_to? :revert
656
881
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
657
882
  arguments[0] = proper_table_name(arguments.first, table_name_options)
658
883
  if [:rename_table, :add_foreign_key].include?(method) ||
@@ -668,26 +893,29 @@ module ActiveRecord
668
893
 
669
894
  def copy(destination, sources, options = {})
670
895
  copied = []
896
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
671
897
 
672
898
  FileUtils.mkdir_p(destination) unless File.exist?(destination)
673
899
 
674
- destination_migrations = ActiveRecord::Migrator.migrations(destination)
900
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
675
901
  last = destination_migrations.last
676
902
  sources.each do |scope, path|
677
- source_migrations = ActiveRecord::Migrator.migrations(path)
903
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
678
904
 
679
905
  source_migrations.each do |migration|
680
906
  source = File.binread(migration.filename)
681
907
  inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
682
- if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
908
+ magic_comments = +""
909
+ loop do
683
910
  # If we have a magic comment in the original migration,
684
911
  # insert our comment after the first newline(end of the magic comment line)
685
912
  # so the magic keep working.
686
913
  # Note that magic comments must be at the first line(except sh-bang).
687
- source[/\n/] = "\n#{inserted_comment}"
688
- else
689
- source = "#{inserted_comment}#{source}"
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
690
917
  end
918
+ source = "#{magic_comments}#{inserted_comment}#{source}"
691
919
 
692
920
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
693
921
  if options[:on_skip] && duplicate.scope != scope.to_s
@@ -731,7 +959,9 @@ module ActiveRecord
731
959
  end
732
960
  end
733
961
 
734
- def table_name_options(config = ActiveRecord::Base)
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:
735
965
  {
736
966
  table_name_prefix: config.table_name_prefix,
737
967
  table_name_suffix: config.table_name_suffix
@@ -739,19 +969,22 @@ module ActiveRecord
739
969
  end
740
970
 
741
971
  private
742
- def execute_block
743
- if connection.respond_to? :execute_block
744
- super # use normal delegation to record the block
745
- else
746
- yield
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)
747
982
  end
748
- end
749
983
  end
750
984
 
751
985
  # MigrationProxy is used to defer loading of the actual migration classes
752
986
  # until they are needed
753
- class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
754
-
987
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
755
988
  def initialize(name, version, filename, scope)
756
989
  super
757
990
  @migration = nil
@@ -777,7 +1010,6 @@ module ActiveRecord
777
1010
  require(File.expand_path(filename))
778
1011
  name.constantize.new(name, version)
779
1012
  end
780
-
781
1013
  end
782
1014
 
783
1015
  class NullMigration < MigrationProxy #:nodoc:
@@ -790,164 +1022,189 @@ module ActiveRecord
790
1022
  end
791
1023
  end
792
1024
 
793
- class Migrator#:nodoc:
794
- class << self
795
- attr_writer :migrations_paths
796
- alias :migrations_path= :migrations_paths=
797
-
798
- def migrate(migrations_paths, target_version = nil, &block)
799
- case
800
- when target_version.nil?
801
- up(migrations_paths, target_version, &block)
802
- when current_version == 0 && target_version == 0
803
- []
804
- when current_version > target_version
805
- down(migrations_paths, target_version, &block)
806
- else
807
- up(migrations_paths, target_version, &block)
808
- end
809
- end
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
810
1027
 
811
- def rollback(migrations_paths, steps=1)
812
- move(:down, migrations_paths, steps)
813
- end
1028
+ def initialize(migrations_paths, schema_migration)
1029
+ @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1031
+ end
814
1032
 
815
- def forward(migrations_paths, steps=1)
816
- 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)
817
1043
  end
1044
+ end
818
1045
 
819
- def up(migrations_paths, target_version = nil)
820
- migrations = migrations(migrations_paths)
821
- migrations.select! { |m| yield m } if block_given?
822
-
823
- new(:up, migrations, target_version).migrate
824
- end
1046
+ def rollback(steps = 1)
1047
+ move(:down, steps)
1048
+ end
825
1049
 
826
- def down(migrations_paths, target_version = nil, &block)
827
- migrations = migrations(migrations_paths)
828
- migrations.select! { |m| yield m } if block_given?
1050
+ def forward(steps = 1)
1051
+ move(:up, steps)
1052
+ end
829
1053
 
830
- new(:down, migrations, target_version).migrate
1054
+ def up(target_version = nil)
1055
+ selected_migrations = if block_given?
1056
+ migrations.select { |m| yield m }
1057
+ else
1058
+ migrations
831
1059
  end
832
1060
 
833
- def run(direction, migrations_paths, target_version)
834
- new(direction, migrations(migrations_paths), target_version).run
835
- end
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1062
+ end
836
1063
 
837
- def open(migrations_paths)
838
- new(:up, migrations(migrations_paths), nil)
1064
+ def down(target_version = nil)
1065
+ selected_migrations = if block_given?
1066
+ migrations.select { |m| yield m }
1067
+ else
1068
+ migrations
839
1069
  end
840
1070
 
841
- def schema_migrations_table_name
842
- SchemaMigration.table_name
843
- end
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1072
+ end
844
1073
 
845
- def get_all_versions(connection = Base.connection)
846
- if connection.table_exists?(schema_migrations_table_name)
847
- SchemaMigration.all.map { |x| x.version.to_i }.sort
848
- else
849
- []
850
- end
851
- end
1074
+ def run(direction, target_version)
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1076
+ end
852
1077
 
853
- def current_version(connection = Base.connection)
854
- get_all_versions(connection).max || 0
855
- end
1078
+ def open
1079
+ Migrator.new(:up, migrations, schema_migration)
1080
+ end
856
1081
 
857
- def needs_migration?(connection = Base.connection)
858
- (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
1082
+ def get_all_versions
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1085
+ else
1086
+ []
859
1087
  end
1088
+ end
860
1089
 
861
- def any_migrations?
862
- migrations(migrations_paths).any?
863
- end
1090
+ def current_version
1091
+ get_all_versions.max || 0
1092
+ rescue ActiveRecord::NoDatabaseError
1093
+ end
864
1094
 
865
- def last_version
866
- last_migration.version
867
- end
1095
+ def needs_migration?
1096
+ (migrations.collect(&:version) - get_all_versions).size > 0
1097
+ end
868
1098
 
869
- def last_migration #:nodoc:
870
- migrations(migrations_paths).last || NullMigration.new
871
- end
1099
+ def any_migrations?
1100
+ migrations.any?
1101
+ end
872
1102
 
873
- def migrations_paths
874
- @migrations_paths ||= ['db/migrate']
875
- # just to not break things if someone uses: migration_path = some_string
876
- Array(@migrations_paths)
877
- end
1103
+ def last_migration #:nodoc:
1104
+ migrations.last || NullMigration.new
1105
+ end
878
1106
 
879
- def migrations_path
880
- migrations_paths.first
881
- end
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
882
1113
 
883
- def parse_migration_filename(filename) # :nodoc:
884
- File.basename(filename).scan(/\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
1114
+ MigrationProxy.new(name, version, file, scope)
885
1115
  end
886
1116
 
887
- def migrations(paths)
888
- paths = Array(paths)
1117
+ migrations.sort_by(&:version)
1118
+ end
889
1119
 
890
- migrations = migration_files(paths).map do |file|
891
- version, name, scope = parse_migration_filename(file)
892
- raise IllegalMigrationNameError.new(file) unless version
893
- version = version.to_i
894
- name = name.camelize
1120
+ def migrations_status
1121
+ db_list = schema_migration.normalized_versions
895
1122
 
896
- MigrationProxy.new(name, version, file, scope)
897
- end
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
898
1130
 
899
- migrations.sort_by(&:version)
1131
+ db_list.map! do |version|
1132
+ ["up", version, "********** NO FILE **********"]
900
1133
  end
901
1134
 
902
- def migrations_status(paths)
903
- paths = Array(paths)
1135
+ (db_list + file_list).sort_by { |_, version, _| version }
1136
+ end
904
1137
 
905
- db_list = ActiveRecord::SchemaMigration.normalized_versions
1138
+ def current_environment
1139
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1140
+ end
906
1141
 
907
- file_list = migration_files(paths).map do |file|
908
- version, name, scope = parse_migration_filename(file)
909
- raise IllegalMigrationNameError.new(file) unless version
910
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
911
- status = db_list.delete(version) ? "up" : "down"
912
- [status, version, (name + scope).humanize]
913
- end.compact
1142
+ def protected_environment?
1143
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1144
+ end
914
1145
 
915
- db_list.map! do |version|
916
- ["up", version, "********** NO FILE **********"]
917
- end
1146
+ def last_stored_environment
1147
+ return nil if current_version == 0
1148
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
918
1149
 
919
- (db_list + file_list).sort_by { |_, version, _| version }
920
- end
1150
+ environment = ActiveRecord::InternalMetadata[:environment]
1151
+ raise NoEnvironmentInSchemaError unless environment
1152
+ environment
1153
+ end
921
1154
 
922
- def migration_files(paths)
1155
+ private
1156
+ def migration_files
1157
+ paths = Array(migrations_paths)
923
1158
  Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
924
1159
  end
925
1160
 
926
- private
1161
+ def parse_migration_filename(filename)
1162
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1163
+ end
927
1164
 
928
- def move(direction, migrations_paths, steps)
929
- migrator = new(direction, migrations(migrations_paths))
930
- start_index = migrator.migrations.index(migrator.current_migration)
1165
+ def move(direction, steps)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
931
1167
 
932
- if start_index
933
- finish = migrator.migrations[start_index + steps]
934
- version = finish ? finish.version : 0
935
- send(direction, migrations_paths, version)
1168
+ if current_version != 0 && !migrator.current_migration
1169
+ raise UnknownMigrationVersionError.new(current_version)
936
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
937
1192
  end
938
1193
  end
939
1194
 
940
- def initialize(direction, migrations, target_version = nil)
941
- raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
1195
+ self.migrations_paths = ["db/migrate"]
942
1196
 
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
943
1198
  @direction = direction
944
1199
  @target_version = target_version
945
1200
  @migrated_versions = nil
946
1201
  @migrations = migrations
1202
+ @schema_migration = schema_migration
947
1203
 
948
1204
  validate(@migrations)
949
1205
 
950
- Base.connection.initialize_schema_migrations_table
1206
+ @schema_migration.create_table
1207
+ ActiveRecord::InternalMetadata.create_table
951
1208
  end
952
1209
 
953
1210
  def current_version
@@ -960,32 +1217,18 @@ module ActiveRecord
960
1217
  alias :current :current_migration
961
1218
 
962
1219
  def run
963
- migration = migrations.detect { |m| m.version == @target_version }
964
- raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
965
- unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
966
- begin
967
- execute_migration_in_transaction(migration, @direction)
968
- rescue => e
969
- canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
970
- raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
971
- end
1220
+ if use_advisory_lock?
1221
+ with_advisory_lock { run_without_lock }
1222
+ else
1223
+ run_without_lock
972
1224
  end
973
1225
  end
974
1226
 
975
1227
  def migrate
976
- if !target && @target_version && @target_version > 0
977
- raise UnknownMigrationVersionError.new(@target_version)
978
- end
979
-
980
- runnable.each do |migration|
981
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
982
-
983
- begin
984
- execute_migration_in_transaction(migration, @direction)
985
- rescue => e
986
- canceled_msg = use_transaction?(migration) ? "this and " : ""
987
- raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
988
- end
1228
+ if use_advisory_lock?
1229
+ with_advisory_lock { migrate_without_lock }
1230
+ else
1231
+ migrate_without_lock
989
1232
  end
990
1233
  end
991
1234
 
@@ -1010,70 +1253,145 @@ module ActiveRecord
1010
1253
  end
1011
1254
 
1012
1255
  def migrated
1013
- @migrated_versions ||= Set.new(self.class.get_all_versions)
1256
+ @migrated_versions || load_migrated
1014
1257
  end
1015
1258
 
1016
- private
1017
- def ran?(migration)
1018
- migrated.include?(migration.version.to_i)
1259
+ def load_migrated
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1019
1261
  end
1020
1262
 
1021
- def execute_migration_in_transaction(migration, direction)
1022
- ddl_transaction(migration) do
1023
- migration.migrate(direction)
1024
- record_version_state_after_migrating(migration.version)
1263
+ private
1264
+
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)
1270
+
1271
+ record_environment
1272
+ result
1025
1273
  end
1026
- end
1027
1274
 
1028
- def target
1029
- migrations.detect { |m| m.version == @target_version }
1030
- end
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)
1279
+ end
1031
1280
 
1032
- def finish
1033
- migrations.index(target) || migrations.size - 1
1034
- end
1281
+ result = runnable.each do |migration|
1282
+ execute_migration_in_transaction(migration, @direction)
1283
+ end
1035
1284
 
1036
- def start
1037
- up? ? 0 : (migrations.index(current) || 0)
1038
- end
1285
+ record_environment
1286
+ result
1287
+ end
1039
1288
 
1040
- def validate(migrations)
1041
- name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
1042
- raise DuplicateMigrationNameError.new(name) if name
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
1293
+ end
1043
1294
 
1044
- version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
1045
- raise DuplicateMigrationVersionError.new(version) if version
1046
- end
1295
+ def ran?(migration)
1296
+ migrated.include?(migration.version.to_i)
1297
+ end
1047
1298
 
1048
- def record_version_state_after_migrating(version)
1049
- if down?
1050
- migrated.delete(version)
1051
- ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
1052
- else
1053
- migrated << version
1054
- ActiveRecord::SchemaMigration.create!(:version => version.to_s)
1299
+ # Return true if a valid version is not provided.
1300
+ def invalid_target?
1301
+ @target_version && @target_version != 0 && !target
1055
1302
  end
1056
- end
1057
1303
 
1058
- def up?
1059
- @direction == :up
1060
- end
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)
1061
1307
 
1062
- def down?
1063
- @direction == :down
1064
- end
1308
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1065
1309
 
1066
- # Wrap the migration in a transaction only if supported by the adapter.
1067
- def ddl_transaction(migration)
1068
- if use_transaction?(migration)
1069
- Base.transaction { yield }
1070
- else
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
1340
+
1341
+ def record_version_state_after_migrating(version)
1342
+ if down?
1343
+ migrated.delete(version)
1344
+ @schema_migration.delete_by(version: version.to_s)
1345
+ else
1346
+ migrated << version
1347
+ @schema_migration.create!(version: version.to_s)
1348
+ end
1349
+ end
1350
+
1351
+ def up?
1352
+ @direction == :up
1353
+ end
1354
+
1355
+ def down?
1356
+ @direction == :down
1357
+ end
1358
+
1359
+ # Wrap the migration in a transaction only if supported by the adapter.
1360
+ def ddl_transaction(migration)
1361
+ if use_transaction?(migration)
1362
+ Base.transaction { yield }
1363
+ else
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
1071
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
+ )
1388
+ end
1072
1389
  end
1073
- end
1074
1390
 
1075
- def use_transaction?(migration)
1076
- !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1077
- end
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
1078
1396
  end
1079
1397
  end