activerecord 5.0.7.2 → 6.1.1

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

Potentially problematic release.


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

Files changed (363) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +829 -2015
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +11 -9
  5. data/examples/performance.rb +31 -29
  6. data/examples/simple.rb +5 -3
  7. data/lib/active_record.rb +37 -29
  8. data/lib/active_record/aggregations.rb +249 -247
  9. data/lib/active_record/association_relation.rb +30 -18
  10. data/lib/active_record/associations.rb +1714 -1596
  11. data/lib/active_record/associations/alias_tracker.rb +36 -42
  12. data/lib/active_record/associations/association.rb +143 -68
  13. data/lib/active_record/associations/association_scope.rb +98 -94
  14. data/lib/active_record/associations/belongs_to_association.rb +76 -46
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
  16. data/lib/active_record/associations/builder/association.rb +27 -28
  17. data/lib/active_record/associations/builder/belongs_to.rb +52 -60
  18. data/lib/active_record/associations/builder/collection_association.rb +12 -22
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
  20. data/lib/active_record/associations/builder/has_many.rb +10 -2
  21. data/lib/active_record/associations/builder/has_one.rb +35 -2
  22. data/lib/active_record/associations/builder/singular_association.rb +5 -1
  23. data/lib/active_record/associations/collection_association.rb +104 -259
  24. data/lib/active_record/associations/collection_proxy.rb +169 -125
  25. data/lib/active_record/associations/foreign_association.rb +22 -0
  26. data/lib/active_record/associations/has_many_association.rb +46 -31
  27. data/lib/active_record/associations/has_many_through_association.rb +66 -46
  28. data/lib/active_record/associations/has_one_association.rb +71 -52
  29. data/lib/active_record/associations/has_one_through_association.rb +20 -11
  30. data/lib/active_record/associations/join_dependency.rb +169 -180
  31. data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
  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 +97 -104
  35. data/lib/active_record/associations/preloader/association.rb +109 -97
  36. data/lib/active_record/associations/preloader/through_association.rb +77 -76
  37. data/lib/active_record/associations/singular_association.rb +12 -45
  38. data/lib/active_record/associations/through_association.rb +27 -15
  39. data/lib/active_record/attribute_assignment.rb +55 -60
  40. data/lib/active_record/attribute_methods.rb +111 -141
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
  42. data/lib/active_record/attribute_methods/dirty.rb +172 -112
  43. data/lib/active_record/attribute_methods/primary_key.rb +88 -91
  44. data/lib/active_record/attribute_methods/query.rb +6 -8
  45. data/lib/active_record/attribute_methods/read.rb +18 -50
  46. data/lib/active_record/attribute_methods/serialization.rb +38 -10
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
  48. data/lib/active_record/attribute_methods/write.rb +25 -32
  49. data/lib/active_record/attributes.rb +69 -31
  50. data/lib/active_record/autosave_association.rb +102 -66
  51. data/lib/active_record/base.rb +16 -25
  52. data/lib/active_record/callbacks.rb +202 -43
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +11 -12
  55. data/lib/active_record/connection_adapters.rb +50 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
  69. data/lib/active_record/connection_adapters/column.rb +55 -13
  70. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  71. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
  82. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  83. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
  85. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
  86. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
  93. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
  95. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
  97. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
  98. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
  104. data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
  106. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
  107. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  108. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
  109. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
  110. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
  121. data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
  123. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  124. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
  131. data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
  132. data/lib/active_record/connection_handling.rb +287 -45
  133. data/lib/active_record/core.rb +385 -181
  134. data/lib/active_record/counter_cache.rb +60 -28
  135. data/lib/active_record/database_configurations.rb +272 -0
  136. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  137. data/lib/active_record/database_configurations/database_config.rb +80 -0
  138. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  139. data/lib/active_record/database_configurations/url_config.rb +53 -0
  140. data/lib/active_record/delegated_type.rb +209 -0
  141. data/lib/active_record/destroy_association_async_job.rb +36 -0
  142. data/lib/active_record/dynamic_matchers.rb +87 -87
  143. data/lib/active_record/enum.rb +122 -47
  144. data/lib/active_record/errors.rb +153 -22
  145. data/lib/active_record/explain.rb +13 -8
  146. data/lib/active_record/explain_registry.rb +3 -1
  147. data/lib/active_record/explain_subscriber.rb +9 -4
  148. data/lib/active_record/fixture_set/file.rb +20 -22
  149. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  150. data/lib/active_record/fixture_set/render_context.rb +17 -0
  151. data/lib/active_record/fixture_set/table_row.rb +152 -0
  152. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  153. data/lib/active_record/fixtures.rb +246 -507
  154. data/lib/active_record/gem_version.rb +6 -4
  155. data/lib/active_record/inheritance.rb +168 -95
  156. data/lib/active_record/insert_all.rb +208 -0
  157. data/lib/active_record/integration.rb +114 -25
  158. data/lib/active_record/internal_metadata.rb +30 -24
  159. data/lib/active_record/legacy_yaml_adapter.rb +11 -5
  160. data/lib/active_record/locking/optimistic.rb +81 -85
  161. data/lib/active_record/locking/pessimistic.rb +22 -6
  162. data/lib/active_record/log_subscriber.rb +68 -31
  163. data/lib/active_record/middleware/database_selector.rb +77 -0
  164. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  165. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  166. data/lib/active_record/migration.rb +439 -342
  167. data/lib/active_record/migration/command_recorder.rb +152 -98
  168. data/lib/active_record/migration/compatibility.rb +229 -60
  169. data/lib/active_record/migration/join_table.rb +8 -7
  170. data/lib/active_record/model_schema.rb +230 -122
  171. data/lib/active_record/nested_attributes.rb +213 -203
  172. data/lib/active_record/no_touching.rb +11 -2
  173. data/lib/active_record/null_relation.rb +12 -34
  174. data/lib/active_record/persistence.rb +471 -97
  175. data/lib/active_record/query_cache.rb +23 -12
  176. data/lib/active_record/querying.rb +43 -25
  177. data/lib/active_record/railtie.rb +155 -43
  178. data/lib/active_record/railties/console_sandbox.rb +2 -0
  179. data/lib/active_record/railties/controller_runtime.rb +34 -33
  180. data/lib/active_record/railties/databases.rake +507 -195
  181. data/lib/active_record/readonly_attributes.rb +9 -4
  182. data/lib/active_record/reflection.rb +245 -269
  183. data/lib/active_record/relation.rb +475 -324
  184. data/lib/active_record/relation/batches.rb +125 -72
  185. data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
  186. data/lib/active_record/relation/calculations.rb +267 -171
  187. data/lib/active_record/relation/delegation.rb +73 -69
  188. data/lib/active_record/relation/finder_methods.rb +238 -248
  189. data/lib/active_record/relation/from_clause.rb +7 -9
  190. data/lib/active_record/relation/merger.rb +95 -77
  191. data/lib/active_record/relation/predicate_builder.rb +109 -110
  192. data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
  193. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
  194. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
  195. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
  196. data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
  197. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  198. data/lib/active_record/relation/query_attribute.rb +33 -2
  199. data/lib/active_record/relation/query_methods.rb +654 -374
  200. data/lib/active_record/relation/record_fetch_warning.rb +8 -6
  201. data/lib/active_record/relation/spawn_methods.rb +15 -14
  202. data/lib/active_record/relation/where_clause.rb +171 -109
  203. data/lib/active_record/result.rb +88 -51
  204. data/lib/active_record/runtime_registry.rb +5 -3
  205. data/lib/active_record/sanitization.rb +73 -100
  206. data/lib/active_record/schema.rb +7 -14
  207. data/lib/active_record/schema_dumper.rb +101 -69
  208. data/lib/active_record/schema_migration.rb +16 -12
  209. data/lib/active_record/scoping.rb +20 -20
  210. data/lib/active_record/scoping/default.rb +92 -95
  211. data/lib/active_record/scoping/named.rb +39 -30
  212. data/lib/active_record/secure_token.rb +19 -9
  213. data/lib/active_record/serialization.rb +7 -3
  214. data/lib/active_record/signed_id.rb +116 -0
  215. data/lib/active_record/statement_cache.rb +80 -29
  216. data/lib/active_record/store.rb +122 -42
  217. data/lib/active_record/suppressor.rb +6 -3
  218. data/lib/active_record/table_metadata.rb +51 -39
  219. data/lib/active_record/tasks/database_tasks.rb +332 -115
  220. data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
  221. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
  222. data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
  223. data/lib/active_record/test_databases.rb +24 -0
  224. data/lib/active_record/test_fixtures.rb +246 -0
  225. data/lib/active_record/timestamp.rb +70 -38
  226. data/lib/active_record/touch_later.rb +26 -24
  227. data/lib/active_record/transactions.rb +121 -184
  228. data/lib/active_record/translation.rb +3 -1
  229. data/lib/active_record/type.rb +29 -17
  230. data/lib/active_record/type/adapter_specific_registry.rb +44 -48
  231. data/lib/active_record/type/date.rb +2 -0
  232. data/lib/active_record/type/date_time.rb +2 -0
  233. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  234. data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
  235. data/lib/active_record/type/internal/timezone.rb +2 -0
  236. data/lib/active_record/type/json.rb +30 -0
  237. data/lib/active_record/type/serialized.rb +20 -9
  238. data/lib/active_record/type/text.rb +11 -0
  239. data/lib/active_record/type/time.rb +12 -1
  240. data/lib/active_record/type/type_map.rb +14 -17
  241. data/lib/active_record/type/unsigned_integer.rb +16 -0
  242. data/lib/active_record/type_caster.rb +4 -2
  243. data/lib/active_record/type_caster/connection.rb +17 -13
  244. data/lib/active_record/type_caster/map.rb +10 -6
  245. data/lib/active_record/validations.rb +8 -5
  246. data/lib/active_record/validations/absence.rb +2 -0
  247. data/lib/active_record/validations/associated.rb +4 -3
  248. data/lib/active_record/validations/length.rb +2 -0
  249. data/lib/active_record/validations/numericality.rb +35 -0
  250. data/lib/active_record/validations/presence.rb +4 -2
  251. data/lib/active_record/validations/uniqueness.rb +52 -45
  252. data/lib/active_record/version.rb +3 -1
  253. data/lib/arel.rb +54 -0
  254. data/lib/arel/alias_predication.rb +9 -0
  255. data/lib/arel/attributes/attribute.rb +41 -0
  256. data/lib/arel/collectors/bind.rb +29 -0
  257. data/lib/arel/collectors/composite.rb +39 -0
  258. data/lib/arel/collectors/plain_string.rb +20 -0
  259. data/lib/arel/collectors/sql_string.rb +27 -0
  260. data/lib/arel/collectors/substitute_binds.rb +35 -0
  261. data/lib/arel/crud.rb +42 -0
  262. data/lib/arel/delete_manager.rb +18 -0
  263. data/lib/arel/errors.rb +9 -0
  264. data/lib/arel/expressions.rb +29 -0
  265. data/lib/arel/factory_methods.rb +49 -0
  266. data/lib/arel/insert_manager.rb +49 -0
  267. data/lib/arel/math.rb +45 -0
  268. data/lib/arel/nodes.rb +70 -0
  269. data/lib/arel/nodes/and.rb +32 -0
  270. data/lib/arel/nodes/ascending.rb +23 -0
  271. data/lib/arel/nodes/binary.rb +126 -0
  272. data/lib/arel/nodes/bind_param.rb +44 -0
  273. data/lib/arel/nodes/case.rb +55 -0
  274. data/lib/arel/nodes/casted.rb +62 -0
  275. data/lib/arel/nodes/comment.rb +29 -0
  276. data/lib/arel/nodes/count.rb +12 -0
  277. data/lib/arel/nodes/delete_statement.rb +45 -0
  278. data/lib/arel/nodes/descending.rb +23 -0
  279. data/lib/arel/nodes/equality.rb +15 -0
  280. data/lib/arel/nodes/extract.rb +24 -0
  281. data/lib/arel/nodes/false.rb +16 -0
  282. data/lib/arel/nodes/full_outer_join.rb +8 -0
  283. data/lib/arel/nodes/function.rb +44 -0
  284. data/lib/arel/nodes/grouping.rb +11 -0
  285. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  286. data/lib/arel/nodes/in.rb +15 -0
  287. data/lib/arel/nodes/infix_operation.rb +92 -0
  288. data/lib/arel/nodes/inner_join.rb +8 -0
  289. data/lib/arel/nodes/insert_statement.rb +37 -0
  290. data/lib/arel/nodes/join_source.rb +20 -0
  291. data/lib/arel/nodes/matches.rb +18 -0
  292. data/lib/arel/nodes/named_function.rb +23 -0
  293. data/lib/arel/nodes/node.rb +51 -0
  294. data/lib/arel/nodes/node_expression.rb +13 -0
  295. data/lib/arel/nodes/ordering.rb +27 -0
  296. data/lib/arel/nodes/outer_join.rb +8 -0
  297. data/lib/arel/nodes/over.rb +15 -0
  298. data/lib/arel/nodes/regexp.rb +16 -0
  299. data/lib/arel/nodes/right_outer_join.rb +8 -0
  300. data/lib/arel/nodes/select_core.rb +67 -0
  301. data/lib/arel/nodes/select_statement.rb +41 -0
  302. data/lib/arel/nodes/sql_literal.rb +19 -0
  303. data/lib/arel/nodes/string_join.rb +11 -0
  304. data/lib/arel/nodes/table_alias.rb +31 -0
  305. data/lib/arel/nodes/terminal.rb +16 -0
  306. data/lib/arel/nodes/true.rb +16 -0
  307. data/lib/arel/nodes/unary.rb +44 -0
  308. data/lib/arel/nodes/unary_operation.rb +20 -0
  309. data/lib/arel/nodes/unqualified_column.rb +22 -0
  310. data/lib/arel/nodes/update_statement.rb +41 -0
  311. data/lib/arel/nodes/values_list.rb +9 -0
  312. data/lib/arel/nodes/window.rb +126 -0
  313. data/lib/arel/nodes/with.rb +11 -0
  314. data/lib/arel/order_predications.rb +13 -0
  315. data/lib/arel/predications.rb +250 -0
  316. data/lib/arel/select_manager.rb +270 -0
  317. data/lib/arel/table.rb +118 -0
  318. data/lib/arel/tree_manager.rb +72 -0
  319. data/lib/arel/update_manager.rb +34 -0
  320. data/lib/arel/visitors.rb +13 -0
  321. data/lib/arel/visitors/dot.rb +308 -0
  322. data/lib/arel/visitors/mysql.rb +93 -0
  323. data/lib/arel/visitors/postgresql.rb +120 -0
  324. data/lib/arel/visitors/sqlite.rb +38 -0
  325. data/lib/arel/visitors/to_sql.rb +899 -0
  326. data/lib/arel/visitors/visitor.rb +45 -0
  327. data/lib/arel/window_predications.rb +9 -0
  328. data/lib/rails/generators/active_record.rb +7 -5
  329. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  330. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  331. data/lib/rails/generators/active_record/migration.rb +22 -3
  332. data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
  333. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
  334. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
  335. data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
  336. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  337. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
  338. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  339. metadata +141 -57
  340. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  341. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  342. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  343. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  344. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  345. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  346. data/lib/active_record/associations/preloader/singular_association.rb +0 -20
  347. data/lib/active_record/attribute.rb +0 -213
  348. data/lib/active_record/attribute/user_provided_default.rb +0 -28
  349. data/lib/active_record/attribute_decorators.rb +0 -67
  350. data/lib/active_record/attribute_mutation_tracker.rb +0 -70
  351. data/lib/active_record/attribute_set.rb +0 -110
  352. data/lib/active_record/attribute_set/builder.rb +0 -132
  353. data/lib/active_record/collection_cache_key.rb +0 -50
  354. data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
  355. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
  356. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
  357. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  358. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  359. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
  360. data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
  361. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
  362. data/lib/active_record/relation/where_clause_factory.rb +0 -38
  363. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -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,29 @@ module ActiveRecord
51
53
  # end
52
54
  #
53
55
  # Database-specific information on row locking:
54
- # MySQL: http://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
55
- # PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ #
57
+ # [MySQL]
58
+ # https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
59
+ #
60
+ # [PostgreSQL]
61
+ # https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
62
  module Pessimistic
57
63
  # Obtain a row lock on this record. Reloads the record to obtain the requested
58
64
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
59
65
  # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
60
66
  # the locked record.
61
67
  def lock!(lock = true)
62
- reload(:lock => lock) if persisted?
68
+ if persisted?
69
+ if has_changes_to_save?
70
+ raise(<<-MSG.squish)
71
+ Locking a record with unpersisted changes is not supported. Use
72
+ `save` to persist the changes, or `reload` to discard them
73
+ explicitly.
74
+ MSG
75
+ end
76
+
77
+ reload(lock: lock)
78
+ end
63
79
  self
64
80
  end
65
81
 
@@ -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,9 +19,13 @@ module ActiveRecord
15
19
  rt
16
20
  end
17
21
 
18
- def initialize
19
- super
20
- @odd = false
22
+ def strict_loading_violation(event)
23
+ debug do
24
+ owner = event.payload[:owner]
25
+ association = event.payload[:association]
26
+
27
+ color("Strict loading violation: #{association} lazily loaded on #{owner}.", RED)
28
+ end
21
29
  end
22
30
 
23
31
  def sql(event)
@@ -29,48 +37,57 @@ module ActiveRecord
29
37
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
30
38
 
31
39
  name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
40
+ name = "CACHE #{name}" if payload[:cached]
32
41
  sql = payload[:sql]
33
42
  binds = nil
34
43
 
35
- unless (payload[:binds] || []).empty?
44
+ if payload[:binds]&.any?
36
45
  casted_params = type_casted_binds(payload[:type_casted_binds])
37
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
- render_bind(attr, value)
39
- }.inspect
46
+
47
+ binds = []
48
+ payload[:binds].each_with_index do |attr, i|
49
+ binds << render_bind(attr, casted_params[i])
50
+ end
51
+ binds = binds.inspect
52
+ binds.prepend(" ")
40
53
  end
41
54
 
42
55
  name = colorize_payload_name(name, payload[:name])
43
- sql = color(sql, sql_color(sql), true)
56
+ sql = color(sql, sql_color(sql), true) if colorize_logging
44
57
 
45
58
  debug " #{name} #{sql}#{binds}"
46
59
  end
47
60
 
48
61
  private
62
+ def type_casted_binds(casted_binds)
63
+ casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
64
+ end
49
65
 
50
- def type_casted_binds(casted_binds)
51
- casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
52
- end
66
+ def render_bind(attr, value)
67
+ case attr
68
+ when ActiveModel::Attribute
69
+ if attr.type.binary? && attr.value
70
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
71
+ end
72
+ when Array
73
+ attr = attr.first
74
+ else
75
+ attr = nil
76
+ end
53
77
 
54
- def render_bind(attr, value)
55
- if attr.is_a?(Array)
56
- attr = attr.first
57
- elsif attr.type.binary? && attr.value
58
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
78
+ [attr&.name, value]
59
79
  end
60
80
 
61
- [attr && attr.name, value]
62
- end
63
-
64
- def colorize_payload_name(name, payload_name)
65
- if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
66
- color(name, MAGENTA, true)
67
- else
68
- color(name, CYAN, true)
81
+ def colorize_payload_name(name, payload_name)
82
+ if payload_name.blank? || payload_name == "SQL" # SQL vs Model Load/Exists
83
+ color(name, MAGENTA, true)
84
+ else
85
+ color(name, CYAN, true)
86
+ end
69
87
  end
70
- end
71
88
 
72
- def sql_color(sql)
73
- case sql
89
+ def sql_color(sql)
90
+ case sql
74
91
  when /\A\s*rollback/mi
75
92
  RED
76
93
  when /select .*for update/mi, /\A\s*lock/mi
@@ -87,12 +104,32 @@ module ActiveRecord
87
104
  CYAN
88
105
  else
89
106
  MAGENTA
107
+ end
90
108
  end
91
- end
92
109
 
93
- def logger
94
- ActiveRecord::Base.logger
95
- end
110
+ def logger
111
+ ActiveRecord::Base.logger
112
+ end
113
+
114
+ def debug(progname = nil, &block)
115
+ return unless super
116
+
117
+ if ActiveRecord::Base.verbose_query_logs
118
+ log_query_source
119
+ end
120
+ end
121
+
122
+ def log_query_source
123
+ source = extract_query_source_location(caller)
124
+
125
+ if source
126
+ logger.debug(" ↳ #{source}")
127
+ end
128
+ end
129
+
130
+ def extract_query_source_location(locations)
131
+ backtrace_cleaner.clean(locations.lazy).first
132
+ end
96
133
  end
97
134
  end
98
135
 
@@ -0,0 +1,77 @@
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
+ def select_database(request, &blk)
59
+ context = context_klass.call(request)
60
+ resolver = resolver_klass.call(context, options)
61
+
62
+ response = if reading_request?(request)
63
+ resolver.read(&blk)
64
+ else
65
+ resolver.write(&blk)
66
+ end
67
+
68
+ resolver.update_context(response)
69
+ response
70
+ end
71
+
72
+ def reading_request?(request)
73
+ request.get? || request.head?
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver/session"
4
+ require "active_support/core_ext/numeric/time"
5
+
6
+ module ActiveRecord
7
+ module Middleware
8
+ class DatabaseSelector
9
+ # The Resolver class is used by the DatabaseSelector middleware to
10
+ # determine which database the request should use.
11
+ #
12
+ # To change the behavior of the Resolver class in your application,
13
+ # create a custom resolver class that inherits from
14
+ # DatabaseSelector::Resolver and implements the methods that need to
15
+ # be changed.
16
+ #
17
+ # By default the Resolver class will send read traffic to the replica
18
+ # if it's been 2 seconds since the last write.
19
+ class Resolver # :nodoc:
20
+ SEND_TO_REPLICA_DELAY = 2.seconds
21
+
22
+ def self.call(context, options = {})
23
+ new(context, options)
24
+ end
25
+
26
+ def initialize(context, options = {})
27
+ @context = context
28
+ @options = options
29
+ @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
30
+ @instrumenter = ActiveSupport::Notifications.instrumenter
31
+ end
32
+
33
+ attr_reader :context, :delay, :instrumenter
34
+
35
+ def read(&blk)
36
+ if read_from_primary?
37
+ read_from_primary(&blk)
38
+ else
39
+ read_from_replica(&blk)
40
+ end
41
+ end
42
+
43
+ def write(&blk)
44
+ write_to_primary(&blk)
45
+ end
46
+
47
+ def update_context(response)
48
+ context.save(response)
49
+ end
50
+
51
+ private
52
+ def read_from_primary(&blk)
53
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
54
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
55
+ yield
56
+ end
57
+ end
58
+ end
59
+
60
+ def read_from_replica(&blk)
61
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
62
+ instrumenter.instrument("database_selector.active_record.read_from_replica") do
63
+ yield
64
+ end
65
+ end
66
+ end
67
+
68
+ def write_to_primary(&blk)
69
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
70
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
71
+ yield
72
+ ensure
73
+ context.update_last_write_timestamp
74
+ end
75
+ end
76
+ end
77
+
78
+ def read_from_primary?
79
+ !time_since_last_write_ok?
80
+ end
81
+
82
+ def send_to_replica_delay
83
+ delay
84
+ end
85
+
86
+ def time_since_last_write_ok?
87
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,48 @@
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
+
42
+ def save(response)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
1
4
  require "set"
2
5
  require "zlib"
6
+ require "active_support/core_ext/array/access"
7
+ require "active_support/core_ext/enumerable"
3
8
  require "active_support/core_ext/module/attribute_accessors"
9
+ require "active_support/actionable_error"
4
10
 
5
11
  module ActiveRecord
6
- class MigrationError < ActiveRecordError#:nodoc:
12
+ class MigrationError < ActiveRecordError #:nodoc:
7
13
  def initialize(message = nil)
8
14
  message = "\n\n#{message}\n\n" if message
9
15
  super
@@ -14,13 +20,13 @@ module ActiveRecord
14
20
  # For example the following migration is not reversible.
15
21
  # Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
16
22
  #
17
- # class IrreversibleMigrationExample < ActiveRecord::Migration[5.0]
23
+ # class IrreversibleMigrationExample < ActiveRecord::Migration[6.0]
18
24
  # def change
19
25
  # create_table :distributors do |t|
20
26
  # t.string :zipcode
21
27
  # end
22
28
  #
23
- # execute <<-SQL
29
+ # execute <<~SQL
24
30
  # ALTER TABLE distributors
25
31
  # ADD CONSTRAINT zipchk
26
32
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -32,13 +38,13 @@ module ActiveRecord
32
38
  #
33
39
  # 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
34
40
  #
35
- # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
41
+ # class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
36
42
  # def up
37
43
  # create_table :distributors do |t|
38
44
  # t.string :zipcode
39
45
  # end
40
46
  #
41
- # execute <<-SQL
47
+ # execute <<~SQL
42
48
  # ALTER TABLE distributors
43
49
  # ADD CONSTRAINT zipchk
44
50
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -46,7 +52,7 @@ module ActiveRecord
46
52
  # end
47
53
  #
48
54
  # def down
49
- # execute <<-SQL
55
+ # execute <<~SQL
50
56
  # ALTER TABLE distributors
51
57
  # DROP CONSTRAINT zipchk
52
58
  # SQL
@@ -57,7 +63,7 @@ module ActiveRecord
57
63
  #
58
64
  # 2. Use the #reversible method in <tt>#change</tt> method:
59
65
  #
60
- # class ReversibleMigrationExample < ActiveRecord::Migration[5.0]
66
+ # class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
61
67
  # def change
62
68
  # create_table :distributors do |t|
63
69
  # t.string :zipcode
@@ -65,7 +71,7 @@ module ActiveRecord
65
71
  #
66
72
  # reversible do |dir|
67
73
  # dir.up do
68
- # execute <<-SQL
74
+ # execute <<~SQL
69
75
  # ALTER TABLE distributors
70
76
  # ADD CONSTRAINT zipchk
71
77
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -73,7 +79,7 @@ module ActiveRecord
73
79
  # end
74
80
  #
75
81
  # dir.down do
76
- # execute <<-SQL
82
+ # execute <<~SQL
77
83
  # ALTER TABLE distributors
78
84
  # DROP CONSTRAINT zipchk
79
85
  # SQL
@@ -84,7 +90,7 @@ module ActiveRecord
84
90
  class IrreversibleMigration < MigrationError
85
91
  end
86
92
 
87
- class DuplicateMigrationVersionError < MigrationError#:nodoc:
93
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
88
94
  def initialize(version = nil)
89
95
  if version
90
96
  super("Multiple migrations have the version number #{version}.")
@@ -94,7 +100,7 @@ module ActiveRecord
94
100
  end
95
101
  end
96
102
 
97
- class DuplicateMigrationNameError < MigrationError#:nodoc:
103
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
98
104
  def initialize(name = nil)
99
105
  if name
100
106
  super("Multiple migrations have the name #{name}.")
@@ -114,7 +120,7 @@ module ActiveRecord
114
120
  end
115
121
  end
116
122
 
117
- class IllegalMigrationNameError < MigrationError#:nodoc:
123
+ class IllegalMigrationNameError < MigrationError #:nodoc:
118
124
  def initialize(name = nil)
119
125
  if name
120
126
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
@@ -124,20 +130,44 @@ module ActiveRecord
124
130
  end
125
131
  end
126
132
 
127
- class PendingMigrationError < MigrationError#:nodoc:
128
- def initialize(message = nil)
129
- if !message && defined?(Rails.env)
130
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
131
- elsif !message
132
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
133
- else
134
- super
133
+ class PendingMigrationError < MigrationError #:nodoc:
134
+ include ActiveSupport::ActionableError
135
+
136
+ action "Run pending migrations" do
137
+ ActiveRecord::Tasks::DatabaseTasks.migrate
138
+
139
+ if ActiveRecord::Base.dump_schema_after_migration
140
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(
141
+ ActiveRecord::Base.connection_db_config
142
+ )
135
143
  end
136
144
  end
145
+
146
+ def initialize(message = nil)
147
+ super(message || detailed_migration_message)
148
+ end
149
+
150
+ private
151
+ def detailed_migration_message
152
+ message = "Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate"
153
+ message += " RAILS_ENV=#{::Rails.env}" if defined?(Rails.env)
154
+ message += "\n\n"
155
+
156
+ pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
157
+
158
+ message += "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}\n\n"
159
+
160
+ pending_migrations.each do |pending_migration|
161
+ message += "#{pending_migration.basename}\n"
162
+ end
163
+
164
+ message
165
+ end
137
166
  end
138
167
 
139
168
  class ConcurrentMigrationError < MigrationError #:nodoc:
140
- DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running.".freeze
169
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
170
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
141
171
 
142
172
  def initialize(message = DEFAULT_MESSAGE)
143
173
  super
@@ -157,7 +187,7 @@ module ActiveRecord
157
187
 
158
188
  class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
159
189
  def initialize(env = "production")
160
- msg = "You are attempting to run a destructive action against your '#{env}' database.\n"
190
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
161
191
  msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
162
192
  msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
163
193
  super(msg)
@@ -166,7 +196,7 @@ module ActiveRecord
166
196
 
167
197
  class EnvironmentMismatchError < ActiveRecordError
168
198
  def initialize(current: nil, stored: nil)
169
- msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
199
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
170
200
  msg << "You are running in `#{ current }` environment. "
171
201
  msg << "If you are sure you want to continue, first set the environment using:\n\n"
172
202
  msg << " bin/rails db:environment:set"
@@ -178,6 +208,14 @@ module ActiveRecord
178
208
  end
179
209
  end
180
210
 
211
+ class EnvironmentStorageError < ActiveRecordError # :nodoc:
212
+ def initialize
213
+ msg = +"You are attempting to store the environment in a database where metadata is disabled.\n"
214
+ msg << "Check your database configuration to see if this is intended."
215
+ super(msg)
216
+ end
217
+ end
218
+
181
219
  # = Active Record Migrations
182
220
  #
183
221
  # Migrations can manage the evolution of a schema used by several physical
@@ -190,7 +228,7 @@ module ActiveRecord
190
228
  #
191
229
  # Example of a simple migration:
192
230
  #
193
- # class AddSsl < ActiveRecord::Migration[5.0]
231
+ # class AddSsl < ActiveRecord::Migration[6.0]
194
232
  # def up
195
233
  # add_column :accounts, :ssl_enabled, :boolean, default: true
196
234
  # end
@@ -210,7 +248,7 @@ module ActiveRecord
210
248
  #
211
249
  # Example of a more complex migration that also needs to initialize data:
212
250
  #
213
- # class AddSystemSettings < ActiveRecord::Migration[5.0]
251
+ # class AddSystemSettings < ActiveRecord::Migration[6.0]
214
252
  # def up
215
253
  # create_table :system_settings do |t|
216
254
  # t.string :name
@@ -277,8 +315,10 @@ module ActiveRecord
277
315
  #
278
316
  # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
279
317
  # the column to a different type using the same parameters as add_column.
280
- # * <tt>change_column_default(table_name, column_name, default)</tt>: Sets a
281
- # default value for +column_name+ defined by +default+ on +table_name+.
318
+ # * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
319
+ # Sets a default value for +column_name+ defined by +default_or_changes+ on
320
+ # +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
321
+ # as +default_or_changes+ will make this change reversible in the migration.
282
322
  # * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
283
323
  # Sets or removes a +NOT NULL+ constraint on +column_name+. The +null+ flag
284
324
  # indicates whether the value can be +NULL+. See
@@ -302,7 +342,7 @@ module ActiveRecord
302
342
  # named +column_name+ from the table called +table_name+.
303
343
  # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
304
344
  # columns from the table definition.
305
- # * <tt>remove_foreign_key(from_table, options_or_to_table)</tt>: Removes the
345
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
306
346
  # given foreign key from the table called +table_name+.
307
347
  # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
308
348
  # specified by +column_names+.
@@ -324,7 +364,7 @@ module ActiveRecord
324
364
  # The Rails package has several tools to help create and apply migrations.
325
365
  #
326
366
  # To generate a new migration, you can use
327
- # rails generate migration MyNewMigration
367
+ # bin/rails generate migration MyNewMigration
328
368
  #
329
369
  # where MyNewMigration is the name of your migration. The generator will
330
370
  # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
@@ -333,41 +373,36 @@ module ActiveRecord
333
373
  #
334
374
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
335
375
  #
336
- # rails generate migration add_fieldname_to_tablename fieldname:string
376
+ # bin/rails generate migration add_fieldname_to_tablename fieldname:string
337
377
  #
338
378
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
339
- # class AddFieldnameToTablename < ActiveRecord::Migration[5.0]
379
+ # class AddFieldnameToTablename < ActiveRecord::Migration[6.0]
340
380
  # def change
341
381
  # add_column :tablenames, :fieldname, :string
342
382
  # end
343
383
  # end
344
384
  #
345
385
  # To run migrations against the currently configured database, use
346
- # <tt>rails db:migrate</tt>. This will update the database by running all of the
386
+ # <tt>bin/rails db:migrate</tt>. This will update the database by running all of the
347
387
  # pending migrations, creating the <tt>schema_migrations</tt> table
348
388
  # (see "About the schema_migrations table" section below) if missing. It will also
349
- # invoke the db:schema:dump task, which will update your db/schema.rb file
389
+ # invoke the db:schema:dump command, which will update your db/schema.rb file
350
390
  # to match the structure of your database.
351
391
  #
352
392
  # To roll the database back to a previous migration version, use
353
- # <tt>rails db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
393
+ # <tt>bin/rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
354
394
  # you wish to downgrade. Alternatively, you can also use the STEP option if you
355
- # wish to rollback last few migrations. <tt>rails db:migrate STEP=2</tt> will rollback
395
+ # wish to rollback last few migrations. <tt>bin/rails db:rollback STEP=2</tt> will rollback
356
396
  # the latest two migrations.
357
397
  #
358
398
  # If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
359
399
  # that step will fail and you'll have some manual work to do.
360
400
  #
361
- # == Database support
362
- #
363
- # Migrations are currently supported in MySQL, PostgreSQL, SQLite,
364
- # SQL Server, and Oracle (all supported databases except DB2).
365
- #
366
401
  # == More examples
367
402
  #
368
403
  # Not all migrations change the schema. Some just fix the data:
369
404
  #
370
- # class RemoveEmptyTags < ActiveRecord::Migration[5.0]
405
+ # class RemoveEmptyTags < ActiveRecord::Migration[6.0]
371
406
  # def up
372
407
  # Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
373
408
  # end
@@ -380,7 +415,7 @@ module ActiveRecord
380
415
  #
381
416
  # Others remove columns when they migrate up instead of down:
382
417
  #
383
- # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[5.0]
418
+ # class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[6.0]
384
419
  # def up
385
420
  # remove_column :items, :incomplete_items_count
386
421
  # remove_column :items, :completed_items_count
@@ -394,7 +429,7 @@ module ActiveRecord
394
429
  #
395
430
  # And sometimes you need to do something in SQL not abstracted directly by migrations:
396
431
  #
397
- # class MakeJoinUnique < ActiveRecord::Migration[5.0]
432
+ # class MakeJoinUnique < ActiveRecord::Migration[6.0]
398
433
  # def up
399
434
  # execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
400
435
  # end
@@ -411,7 +446,7 @@ module ActiveRecord
411
446
  # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
412
447
  # latest column data from after the new column was added. Example:
413
448
  #
414
- # class AddPeopleSalary < ActiveRecord::Migration[5.0]
449
+ # class AddPeopleSalary < ActiveRecord::Migration[6.0]
415
450
  # def up
416
451
  # add_column :people, :salary, :integer
417
452
  # Person.reset_column_information
@@ -469,7 +504,7 @@ module ActiveRecord
469
504
  # To define a reversible migration, define the +change+ method in your
470
505
  # migration like this:
471
506
  #
472
- # class TenderloveMigration < ActiveRecord::Migration[5.0]
507
+ # class TenderloveMigration < ActiveRecord::Migration[6.0]
473
508
  # def change
474
509
  # create_table(:horses) do |t|
475
510
  # t.column :content, :text
@@ -481,9 +516,9 @@ module ActiveRecord
481
516
  # This migration will create the horses table for you on the way up, and
482
517
  # automatically figure out how to drop the table on the way down.
483
518
  #
484
- # Some commands like +remove_column+ cannot be reversed. If you care to
485
- # define how to move up and down in these cases, you should define the +up+
486
- # and +down+ methods as before.
519
+ # Some commands cannot be reversed. If you care to define how to move up
520
+ # and down in these cases, you should define the +up+ and +down+ methods
521
+ # as before.
487
522
  #
488
523
  # If a command cannot be reversed, an
489
524
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -499,7 +534,7 @@ module ActiveRecord
499
534
  # can't execute inside a transaction though, and for these situations
500
535
  # you can turn the automatic transactions off.
501
536
  #
502
- # class ChangeEnum < ActiveRecord::Migration[5.0]
537
+ # class ChangeEnum < ActiveRecord::Migration[6.0]
503
538
  # disable_ddl_transaction!
504
539
  #
505
540
  # def up
@@ -510,17 +545,21 @@ module ActiveRecord
510
545
  # Remember that you can still open your own transactions, even if you
511
546
  # are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
512
547
  class Migration
513
- autoload :CommandRecorder, 'active_record/migration/command_recorder'
514
- autoload :Compatibility, 'active_record/migration/compatibility'
548
+ autoload :CommandRecorder, "active_record/migration/command_recorder"
549
+ autoload :Compatibility, "active_record/migration/compatibility"
550
+ autoload :JoinTable, "active_record/migration/join_table"
515
551
 
516
552
  # This must be defined before the inherited hook, below
517
- class Current < Migration # :nodoc:
553
+ class Current < Migration #:nodoc:
518
554
  end
519
555
 
520
- def self.inherited(subclass) # :nodoc:
556
+ def self.inherited(subclass) #:nodoc:
521
557
  super
522
558
  if subclass.superclass == Migration
523
- subclass.include Compatibility::Legacy
559
+ raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
560
+ "Please specify the Rails release the migration was written for:\n" \
561
+ "\n" \
562
+ " class #{subclass} < ActiveRecord::Migration[4.2]"
524
563
  end
525
564
  end
526
565
 
@@ -532,70 +571,93 @@ module ActiveRecord
532
571
  ActiveRecord::VERSION::STRING.to_f
533
572
  end
534
573
 
535
- MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
574
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
536
575
 
537
576
  # This class is used to verify that all migrations have been run before
538
577
  # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
539
578
  class CheckPending
540
- def initialize(app)
579
+ def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker)
541
580
  @app = app
542
- @last_check = 0
581
+ @needs_check = true
582
+ @mutex = Mutex.new
583
+ @file_watcher = file_watcher
543
584
  end
544
585
 
545
586
  def call(env)
546
- if connection.supports_migrations?
547
- mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
548
- if @last_check < mtime
587
+ @mutex.synchronize do
588
+ @watcher ||= build_watcher do
589
+ @needs_check = true
549
590
  ActiveRecord::Migration.check_pending!(connection)
550
- @last_check = mtime
591
+ @needs_check = false
592
+ end
593
+
594
+ if @needs_check
595
+ @watcher.execute
596
+ else
597
+ @watcher.execute_if_updated
551
598
  end
552
599
  end
600
+
553
601
  @app.call(env)
554
602
  end
555
603
 
556
604
  private
605
+ def build_watcher(&block)
606
+ paths = Array(connection.migration_context.migrations_paths)
607
+ @file_watcher.new([], paths.index_with(["rb"]), &block)
608
+ end
557
609
 
558
- def connection
559
- ActiveRecord::Base.connection
560
- end
610
+ def connection
611
+ ActiveRecord::Base.connection
612
+ end
561
613
  end
562
614
 
563
615
  class << self
564
- attr_accessor :delegate # :nodoc:
565
- attr_accessor :disable_ddl_transaction # :nodoc:
616
+ attr_accessor :delegate #:nodoc:
617
+ attr_accessor :disable_ddl_transaction #:nodoc:
566
618
 
567
- def nearest_delegate # :nodoc:
619
+ def nearest_delegate #:nodoc:
568
620
  delegate || superclass.nearest_delegate
569
621
  end
570
622
 
571
623
  # Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
572
624
  def check_pending!(connection = Base.connection)
573
- raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
625
+ raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
574
626
  end
575
627
 
576
628
  def load_schema_if_pending!
577
- if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations?
629
+ current_db_config = Base.connection_db_config
630
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
631
+
632
+ needs_update = !all_configs.all? do |db_config|
633
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord::Base.schema_format)
634
+ end
635
+
636
+ if needs_update
578
637
  # Roundtrip to Rake to allow plugins to hook into database initialization.
579
- FileUtils.cd Rails.root do
580
- current_config = Base.connection_config
638
+ root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
639
+ FileUtils.cd(root) do
581
640
  Base.clear_all_connections!
582
641
  system("bin/rails db:test:prepare")
583
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
584
- Base.establish_connection(current_config)
585
642
  end
586
- check_pending!
587
643
  end
644
+
645
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
646
+ Base.establish_connection(current_db_config)
647
+
648
+ check_pending!
588
649
  end
589
650
 
590
- def maintain_test_schema! # :nodoc:
651
+ def maintain_test_schema! #:nodoc:
591
652
  if ActiveRecord::Base.maintain_test_schema
592
653
  suppress_messages { load_schema_if_pending! }
593
654
  end
594
655
  end
595
656
 
596
- def method_missing(name, *args, &block) # :nodoc:
657
+ def method_missing(name, *args, &block) #:nodoc:
597
658
  nearest_delegate.send(name, *args, &block)
598
659
  end
660
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
599
661
 
600
662
  def migrate(direction)
601
663
  new.migrate direction
@@ -610,7 +672,7 @@ module ActiveRecord
610
672
  end
611
673
  end
612
674
 
613
- def disable_ddl_transaction # :nodoc:
675
+ def disable_ddl_transaction #:nodoc:
614
676
  self.class.disable_ddl_transaction
615
677
  end
616
678
 
@@ -634,7 +696,7 @@ module ActiveRecord
634
696
  # and create the table 'apples' on the way up, and the reverse
635
697
  # on the way down.
636
698
  #
637
- # class FixTLMigration < ActiveRecord::Migration[5.0]
699
+ # class FixTLMigration < ActiveRecord::Migration[6.0]
638
700
  # def change
639
701
  # revert do
640
702
  # create_table(:horses) do |t|
@@ -651,9 +713,9 @@ module ActiveRecord
651
713
  # Or equivalently, if +TenderloveMigration+ is defined as in the
652
714
  # documentation for Migration:
653
715
  #
654
- # require_relative '20121212123456_tenderlove_migration'
716
+ # require_relative "20121212123456_tenderlove_migration"
655
717
  #
656
- # class FixupTLMigration < ActiveRecord::Migration[5.0]
718
+ # class FixupTLMigration < ActiveRecord::Migration[6.0]
657
719
  # def change
658
720
  # revert TenderloveMigration
659
721
  #
@@ -670,15 +732,13 @@ module ActiveRecord
670
732
  if connection.respond_to? :revert
671
733
  connection.revert { yield }
672
734
  else
673
- recorder = CommandRecorder.new(connection)
735
+ recorder = command_recorder
674
736
  @connection = recorder
675
737
  suppress_messages do
676
738
  connection.revert { yield }
677
739
  end
678
740
  @connection = recorder.delegate
679
- recorder.commands.each do |cmd, args, block|
680
- send(cmd, *args, &block)
681
- end
741
+ recorder.replay(self)
682
742
  end
683
743
  end
684
744
  end
@@ -687,7 +747,7 @@ module ActiveRecord
687
747
  connection.respond_to?(:reverting) && connection.reverting
688
748
  end
689
749
 
690
- class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
750
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
691
751
  def up
692
752
  yield unless reverting
693
753
  end
@@ -706,7 +766,7 @@ module ActiveRecord
706
766
  # when the three columns 'first_name', 'last_name' and 'full_name' exist,
707
767
  # even when migrating down:
708
768
  #
709
- # class SplitNameMigration < ActiveRecord::Migration[5.0]
769
+ # class SplitNameMigration < ActiveRecord::Migration[6.0]
710
770
  # def change
711
771
  # add_column :users, :first_name, :string
712
772
  # add_column :users, :last_name, :string
@@ -725,7 +785,25 @@ module ActiveRecord
725
785
  # end
726
786
  def reversible
727
787
  helper = ReversibleBlockHelper.new(reverting?)
728
- execute_block{ yield helper }
788
+ execute_block { yield helper }
789
+ end
790
+
791
+ # Used to specify an operation that is only run when migrating up
792
+ # (for example, populating a new column with its initial values).
793
+ #
794
+ # In the following example, the new column +published+ will be given
795
+ # the value +true+ for all existing records.
796
+ #
797
+ # class AddPublishedToPosts < ActiveRecord::Migration[6.0]
798
+ # def change
799
+ # add_column :posts, :published, :boolean, default: false
800
+ # up_only do
801
+ # execute "update posts set published = 'true'"
802
+ # end
803
+ # end
804
+ # end
805
+ def up_only
806
+ execute_block { yield } unless reverting?
729
807
  end
730
808
 
731
809
  # Runs the given migration classes.
@@ -767,7 +845,7 @@ module ActiveRecord
767
845
  when :down then announce "reverting"
768
846
  end
769
847
 
770
- time = nil
848
+ time = nil
771
849
  ActiveRecord::Base.connection_pool.with_connection do |conn|
772
850
  time = Benchmark.measure do
773
851
  exec_migration(conn, direction)
@@ -789,13 +867,13 @@ module ActiveRecord
789
867
  change
790
868
  end
791
869
  else
792
- send(direction)
870
+ public_send(direction)
793
871
  end
794
872
  ensure
795
873
  @connection = nil
796
874
  end
797
875
 
798
- def write(text="")
876
+ def write(text = "")
799
877
  puts(text) if verbose
800
878
  end
801
879
 
@@ -805,10 +883,14 @@ module ActiveRecord
805
883
  write "== %s %s" % [text, "=" * length]
806
884
  end
807
885
 
808
- def say(message, subitem=false)
886
+ # Takes a message argument and outputs it as is.
887
+ # A second boolean argument can be passed to specify whether to indent or not.
888
+ def say(message, subitem = false)
809
889
  write "#{subitem ? " ->" : "--"} #{message}"
810
890
  end
811
891
 
892
+ # Outputs text along with how long it took to run its block.
893
+ # If the block returns an integer it assumes it is the number of rows affected.
812
894
  def say_with_time(message)
813
895
  say(message)
814
896
  result = nil
@@ -818,6 +900,7 @@ module ActiveRecord
818
900
  result
819
901
  end
820
902
 
903
+ # Takes a block as an argument and suppresses any output generated by the block.
821
904
  def suppress_messages
822
905
  save, self.verbose = verbose, false
823
906
  yield
@@ -830,7 +913,7 @@ module ActiveRecord
830
913
  end
831
914
 
832
915
  def method_missing(method, *arguments, &block)
833
- arg_list = arguments.map(&:inspect) * ', '
916
+ arg_list = arguments.map(&:inspect) * ", "
834
917
 
835
918
  say_with_time "#{method}(#{arg_list})" do
836
919
  unless connection.respond_to? :revert
@@ -846,29 +929,33 @@ module ActiveRecord
846
929
  connection.send(method, *arguments, &block)
847
930
  end
848
931
  end
932
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
849
933
 
850
934
  def copy(destination, sources, options = {})
851
935
  copied = []
936
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
852
937
 
853
938
  FileUtils.mkdir_p(destination) unless File.exist?(destination)
854
939
 
855
- destination_migrations = ActiveRecord::Migrator.migrations(destination)
940
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
856
941
  last = destination_migrations.last
857
942
  sources.each do |scope, path|
858
- source_migrations = ActiveRecord::Migrator.migrations(path)
943
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
859
944
 
860
945
  source_migrations.each do |migration|
861
946
  source = File.binread(migration.filename)
862
947
  inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
863
- if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
948
+ magic_comments = +""
949
+ loop do
864
950
  # If we have a magic comment in the original migration,
865
951
  # insert our comment after the first newline(end of the magic comment line)
866
952
  # so the magic keep working.
867
953
  # Note that magic comments must be at the first line(except sh-bang).
868
- source[/\n/] = "\n#{inserted_comment}"
869
- else
870
- source = "#{inserted_comment}#{source}"
954
+ source.sub!(/\A(?:#.*\b(?:en)?coding:\s*\S+|#\s*frozen_string_literal:\s*(?:true|false)).*\n/) do |magic_comment|
955
+ magic_comments << magic_comment; ""
956
+ end || break
871
957
  end
958
+ source = "#{magic_comments}#{inserted_comment}#{source}"
872
959
 
873
960
  if duplicate = destination_migrations.detect { |m| m.name == migration.name }
874
961
  if options[:on_skip] && duplicate.scope != scope.to_s
@@ -922,19 +1009,22 @@ module ActiveRecord
922
1009
  end
923
1010
 
924
1011
  private
925
- def execute_block
926
- if connection.respond_to? :execute_block
927
- super # use normal delegation to record the block
928
- else
929
- yield
1012
+ def execute_block
1013
+ if connection.respond_to? :execute_block
1014
+ super # use normal delegation to record the block
1015
+ else
1016
+ yield
1017
+ end
1018
+ end
1019
+
1020
+ def command_recorder
1021
+ CommandRecorder.new(connection)
930
1022
  end
931
- end
932
1023
  end
933
1024
 
934
1025
  # MigrationProxy is used to defer loading of the actual migration classes
935
1026
  # until they are needed
936
- class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
937
-
1027
+ MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
938
1028
  def initialize(name, version, filename, scope)
939
1029
  super
940
1030
  @migration = nil
@@ -944,14 +1034,9 @@ module ActiveRecord
944
1034
  File.basename(filename)
945
1035
  end
946
1036
 
947
- def mtime
948
- File.mtime filename
949
- end
950
-
951
1037
  delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
952
1038
 
953
1039
  private
954
-
955
1040
  def migration
956
1041
  @migration ||= load_migration
957
1042
  end
@@ -960,172 +1045,188 @@ module ActiveRecord
960
1045
  require(File.expand_path(filename))
961
1046
  name.constantize.new(name, version)
962
1047
  end
963
-
964
1048
  end
965
1049
 
966
- class NullMigration < MigrationProxy #:nodoc:
967
- def initialize
968
- super(nil, 0, nil, nil)
969
- end
1050
+ class MigrationContext #:nodoc:
1051
+ attr_reader :migrations_paths, :schema_migration
970
1052
 
971
- def mtime
972
- 0
1053
+ def initialize(migrations_paths, schema_migration)
1054
+ @migrations_paths = migrations_paths
1055
+ @schema_migration = schema_migration
973
1056
  end
974
- end
975
-
976
- class Migrator#:nodoc:
977
- class << self
978
- attr_writer :migrations_paths
979
- alias :migrations_path= :migrations_paths=
980
-
981
- def migrate(migrations_paths, target_version = nil, &block)
982
- case
983
- when target_version.nil?
984
- up(migrations_paths, target_version, &block)
985
- when current_version == 0 && target_version == 0
986
- []
987
- when current_version > target_version
988
- down(migrations_paths, target_version, &block)
989
- else
990
- up(migrations_paths, target_version, &block)
991
- end
992
- end
993
1057
 
994
- def rollback(migrations_paths, steps=1)
995
- move(:down, migrations_paths, steps)
1058
+ def migrate(target_version = nil, &block)
1059
+ case
1060
+ when target_version.nil?
1061
+ up(target_version, &block)
1062
+ when current_version == 0 && target_version == 0
1063
+ []
1064
+ when current_version > target_version
1065
+ down(target_version, &block)
1066
+ else
1067
+ up(target_version, &block)
996
1068
  end
1069
+ end
997
1070
 
998
- def forward(migrations_paths, steps=1)
999
- move(:up, migrations_paths, steps)
1000
- end
1071
+ def rollback(steps = 1)
1072
+ move(:down, steps)
1073
+ end
1001
1074
 
1002
- def up(migrations_paths, target_version = nil)
1003
- migrations = migrations(migrations_paths)
1004
- migrations.select! { |m| yield m } if block_given?
1075
+ def forward(steps = 1)
1076
+ move(:up, steps)
1077
+ end
1005
1078
 
1006
- new(:up, migrations, target_version).migrate
1079
+ def up(target_version = nil)
1080
+ selected_migrations = if block_given?
1081
+ migrations.select { |m| yield m }
1082
+ else
1083
+ migrations
1007
1084
  end
1008
1085
 
1009
- def down(migrations_paths, target_version = nil)
1010
- migrations = migrations(migrations_paths)
1011
- migrations.select! { |m| yield m } if block_given?
1012
-
1013
- new(:down, migrations, target_version).migrate
1014
- end
1086
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1087
+ end
1015
1088
 
1016
- def run(direction, migrations_paths, target_version)
1017
- new(direction, migrations(migrations_paths), target_version).run
1089
+ def down(target_version = nil)
1090
+ selected_migrations = if block_given?
1091
+ migrations.select { |m| yield m }
1092
+ else
1093
+ migrations
1018
1094
  end
1019
1095
 
1020
- def open(migrations_paths)
1021
- new(:up, migrations(migrations_paths), nil)
1022
- end
1096
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1097
+ end
1023
1098
 
1024
- def schema_migrations_table_name
1025
- SchemaMigration.table_name
1026
- end
1099
+ def run(direction, target_version)
1100
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1101
+ end
1027
1102
 
1028
- def get_all_versions(connection = Base.connection)
1029
- ActiveSupport::Deprecation.silence do
1030
- if connection.table_exists?(schema_migrations_table_name)
1031
- SchemaMigration.all.map { |x| x.version.to_i }.sort
1032
- else
1033
- []
1034
- end
1035
- end
1036
- end
1103
+ def open
1104
+ Migrator.new(:up, migrations, schema_migration)
1105
+ end
1037
1106
 
1038
- def current_version(connection = Base.connection)
1039
- get_all_versions(connection).max || 0
1107
+ def get_all_versions
1108
+ if schema_migration.table_exists?
1109
+ schema_migration.all_versions.map(&:to_i)
1110
+ else
1111
+ []
1040
1112
  end
1113
+ end
1041
1114
 
1042
- def needs_migration?(connection = Base.connection)
1043
- (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
1044
- end
1115
+ def current_version
1116
+ get_all_versions.max || 0
1117
+ rescue ActiveRecord::NoDatabaseError
1118
+ end
1045
1119
 
1046
- def any_migrations?
1047
- migrations(migrations_paths).any?
1048
- end
1120
+ def needs_migration?
1121
+ (migrations.collect(&:version) - get_all_versions).size > 0
1122
+ end
1049
1123
 
1050
- def last_migration #:nodoc:
1051
- migrations(migrations_paths).last || NullMigration.new
1052
- end
1124
+ def any_migrations?
1125
+ migrations.any?
1126
+ end
1053
1127
 
1054
- def migrations_paths
1055
- @migrations_paths ||= ['db/migrate']
1056
- # just to not break things if someone uses: migrations_path = some_string
1057
- Array(@migrations_paths)
1058
- end
1128
+ def migrations
1129
+ migrations = migration_files.map do |file|
1130
+ version, name, scope = parse_migration_filename(file)
1131
+ raise IllegalMigrationNameError.new(file) unless version
1132
+ version = version.to_i
1133
+ name = name.camelize
1059
1134
 
1060
- def parse_migration_filename(filename) # :nodoc:
1061
- File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1135
+ MigrationProxy.new(name, version, file, scope)
1062
1136
  end
1063
1137
 
1064
- def migrations(paths)
1065
- paths = Array(paths)
1138
+ migrations.sort_by(&:version)
1139
+ end
1066
1140
 
1067
- migrations = migration_files(paths).map do |file|
1068
- version, name, scope = parse_migration_filename(file)
1069
- raise IllegalMigrationNameError.new(file) unless version
1070
- version = version.to_i
1071
- name = name.camelize
1141
+ def migrations_status
1142
+ db_list = schema_migration.normalized_versions
1072
1143
 
1073
- MigrationProxy.new(name, version, file, scope)
1074
- end
1144
+ file_list = migration_files.map do |file|
1145
+ version, name, scope = parse_migration_filename(file)
1146
+ raise IllegalMigrationNameError.new(file) unless version
1147
+ version = schema_migration.normalize_migration_number(version)
1148
+ status = db_list.delete(version) ? "up" : "down"
1149
+ [status, version, (name + scope).humanize]
1150
+ end.compact
1075
1151
 
1076
- migrations.sort_by(&:version)
1152
+ db_list.map! do |version|
1153
+ ["up", version, "********** NO FILE **********"]
1077
1154
  end
1078
1155
 
1079
- def migrations_status(paths)
1080
- paths = Array(paths)
1156
+ (db_list + file_list).sort_by { |_, version, _| version }
1157
+ end
1081
1158
 
1082
- db_list = ActiveRecord::SchemaMigration.normalized_versions
1159
+ def current_environment
1160
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1161
+ end
1083
1162
 
1084
- file_list = migration_files(paths).map do |file|
1085
- version, name, scope = parse_migration_filename(file)
1086
- raise IllegalMigrationNameError.new(file) unless version
1087
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
1088
- status = db_list.delete(version) ? "up" : "down"
1089
- [status, version, (name + scope).humanize]
1090
- end.compact
1163
+ def protected_environment?
1164
+ ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1165
+ end
1091
1166
 
1092
- db_list.map! do |version|
1093
- ["up", version, "********** NO FILE **********"]
1094
- end
1167
+ def last_stored_environment
1168
+ return nil unless ActiveRecord::InternalMetadata.enabled?
1169
+ return nil if current_version == 0
1170
+ raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
1095
1171
 
1096
- (db_list + file_list).sort_by { |_, version, _| version }
1097
- end
1172
+ environment = ActiveRecord::InternalMetadata[:environment]
1173
+ raise NoEnvironmentInSchemaError unless environment
1174
+ environment
1175
+ end
1098
1176
 
1099
- def migration_files(paths)
1177
+ private
1178
+ def migration_files
1179
+ paths = Array(migrations_paths)
1100
1180
  Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1101
1181
  end
1102
1182
 
1103
- private
1183
+ def parse_migration_filename(filename)
1184
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1185
+ end
1104
1186
 
1105
- def move(direction, migrations_paths, steps)
1106
- migrator = new(direction, migrations(migrations_paths))
1107
- start_index = migrator.migrations.index(migrator.current_migration)
1187
+ def move(direction, steps)
1188
+ migrator = Migrator.new(direction, migrations, schema_migration)
1108
1189
 
1109
- if start_index
1110
- finish = migrator.migrations[start_index + steps]
1111
- version = finish ? finish.version : 0
1112
- send(direction, migrations_paths, version)
1190
+ if current_version != 0 && !migrator.current_migration
1191
+ raise UnknownMigrationVersionError.new(current_version)
1113
1192
  end
1193
+
1194
+ start_index =
1195
+ if current_version == 0
1196
+ 0
1197
+ else
1198
+ migrator.migrations.index(migrator.current_migration)
1199
+ end
1200
+
1201
+ finish = migrator.migrations[start_index + steps]
1202
+ version = finish ? finish.version : 0
1203
+ public_send(direction, version)
1204
+ end
1205
+ end
1206
+
1207
+ class Migrator # :nodoc:
1208
+ class << self
1209
+ attr_accessor :migrations_paths
1210
+
1211
+ # For cases where a table doesn't exist like loading from schema cache
1212
+ def current_version
1213
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
1114
1214
  end
1115
1215
  end
1116
1216
 
1117
- def initialize(direction, migrations, target_version = nil)
1118
- raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
1217
+ self.migrations_paths = ["db/migrate"]
1119
1218
 
1219
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1120
1220
  @direction = direction
1121
1221
  @target_version = target_version
1122
1222
  @migrated_versions = nil
1123
1223
  @migrations = migrations
1224
+ @schema_migration = schema_migration
1124
1225
 
1125
1226
  validate(@migrations)
1126
1227
 
1127
- Base.connection.initialize_schema_migrations_table
1128
- Base.connection.initialize_internal_metadata_table
1228
+ @schema_migration.create_table
1229
+ ActiveRecord::InternalMetadata.create_table
1129
1230
  end
1130
1231
 
1131
1232
  def current_version
@@ -1178,153 +1279,149 @@ module ActiveRecord
1178
1279
  end
1179
1280
 
1180
1281
  def load_migrated
1181
- @migrated_versions = Set.new(self.class.get_all_versions)
1282
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1182
1283
  end
1183
1284
 
1184
1285
  private
1286
+ # Used for running a specific migration.
1287
+ def run_without_lock
1288
+ migration = migrations.detect { |m| m.version == @target_version }
1289
+ raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1290
+ result = execute_migration_in_transaction(migration)
1291
+
1292
+ record_environment
1293
+ result
1294
+ end
1185
1295
 
1186
- # Used for running a specific migration.
1187
- def run_without_lock
1188
- migration = migrations.detect { |m| m.version == @target_version }
1189
- raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
1190
- result = execute_migration_in_transaction(migration, @direction)
1191
-
1192
- record_environment
1193
- result
1194
- end
1296
+ # Used for running multiple migrations up to or down to a certain value.
1297
+ def migrate_without_lock
1298
+ if invalid_target?
1299
+ raise UnknownMigrationVersionError.new(@target_version)
1300
+ end
1195
1301
 
1196
- # Used for running multiple migrations up to or down to a certain value.
1197
- def migrate_without_lock
1198
- if invalid_target?
1199
- raise UnknownMigrationVersionError.new(@target_version)
1302
+ result = runnable.each(&method(:execute_migration_in_transaction))
1303
+ record_environment
1304
+ result
1200
1305
  end
1201
1306
 
1202
- result = runnable.each do |migration|
1203
- execute_migration_in_transaction(migration, @direction)
1307
+ # Stores the current environment in the database.
1308
+ def record_environment
1309
+ return if down?
1310
+ ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.migration_context.current_environment
1204
1311
  end
1205
1312
 
1206
- record_environment
1207
- result
1208
- end
1209
-
1210
- # Stores the current environment in the database.
1211
- def record_environment
1212
- return if down?
1213
- ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
1214
- end
1215
-
1216
- def ran?(migration)
1217
- migrated.include?(migration.version.to_i)
1218
- end
1313
+ def ran?(migration)
1314
+ migrated.include?(migration.version.to_i)
1315
+ end
1219
1316
 
1220
- # Return true if a valid version is not provided.
1221
- def invalid_target?
1222
- !target && @target_version && @target_version > 0
1223
- end
1317
+ # Return true if a valid version is not provided.
1318
+ def invalid_target?
1319
+ @target_version && @target_version != 0 && !target
1320
+ end
1224
1321
 
1225
- def execute_migration_in_transaction(migration, direction)
1226
- return if down? && !migrated.include?(migration.version.to_i)
1227
- return if up? && migrated.include?(migration.version.to_i)
1322
+ def execute_migration_in_transaction(migration)
1323
+ return if down? && !migrated.include?(migration.version.to_i)
1324
+ return if up? && migrated.include?(migration.version.to_i)
1228
1325
 
1229
- Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1326
+ Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
1230
1327
 
1231
- ddl_transaction(migration) do
1232
- migration.migrate(direction)
1233
- record_version_state_after_migrating(migration.version)
1328
+ ddl_transaction(migration) do
1329
+ migration.migrate(@direction)
1330
+ record_version_state_after_migrating(migration.version)
1331
+ end
1332
+ rescue => e
1333
+ msg = +"An error has occurred, "
1334
+ msg << "this and " if use_transaction?(migration)
1335
+ msg << "all later migrations canceled:\n\n#{e}"
1336
+ raise StandardError, msg, e.backtrace
1234
1337
  end
1235
- rescue => e
1236
- msg = "An error has occurred, "
1237
- msg << "this and " if use_transaction?(migration)
1238
- msg << "all later migrations canceled:\n\n#{e}"
1239
- raise StandardError, msg, e.backtrace
1240
- end
1241
-
1242
- def target
1243
- migrations.detect { |m| m.version == @target_version }
1244
- end
1245
1338
 
1246
- def finish
1247
- migrations.index(target) || migrations.size - 1
1248
- end
1339
+ def target
1340
+ migrations.detect { |m| m.version == @target_version }
1341
+ end
1249
1342
 
1250
- def start
1251
- up? ? 0 : (migrations.index(current) || 0)
1252
- end
1343
+ def finish
1344
+ migrations.index(target) || migrations.size - 1
1345
+ end
1253
1346
 
1254
- def validate(migrations)
1255
- name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
1256
- raise DuplicateMigrationNameError.new(name) if name
1347
+ def start
1348
+ up? ? 0 : (migrations.index(current) || 0)
1349
+ end
1257
1350
 
1258
- version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
1259
- raise DuplicateMigrationVersionError.new(version) if version
1260
- end
1351
+ def validate(migrations)
1352
+ name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
1353
+ raise DuplicateMigrationNameError.new(name) if name
1261
1354
 
1262
- def record_version_state_after_migrating(version)
1263
- if down?
1264
- migrated.delete(version)
1265
- ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
1266
- else
1267
- migrated << version
1268
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
1355
+ version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
1356
+ raise DuplicateMigrationVersionError.new(version) if version
1269
1357
  end
1270
- end
1271
1358
 
1272
- def self.last_stored_environment
1273
- return nil if current_version == 0
1274
- raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
1275
-
1276
- environment = ActiveRecord::InternalMetadata[:environment]
1277
- raise NoEnvironmentInSchemaError unless environment
1278
- environment
1279
- end
1359
+ def record_version_state_after_migrating(version)
1360
+ if down?
1361
+ migrated.delete(version)
1362
+ @schema_migration.delete_by(version: version.to_s)
1363
+ else
1364
+ migrated << version
1365
+ @schema_migration.create!(version: version.to_s)
1366
+ end
1367
+ end
1280
1368
 
1281
- def self.current_environment
1282
- ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1283
- end
1369
+ def up?
1370
+ @direction == :up
1371
+ end
1284
1372
 
1285
- def self.protected_environment?
1286
- ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
1287
- end
1373
+ def down?
1374
+ @direction == :down
1375
+ end
1288
1376
 
1289
- def up?
1290
- @direction == :up
1291
- end
1377
+ # Wrap the migration in a transaction only if supported by the adapter.
1378
+ def ddl_transaction(migration)
1379
+ if use_transaction?(migration)
1380
+ Base.transaction { yield }
1381
+ else
1382
+ yield
1383
+ end
1384
+ end
1292
1385
 
1293
- def down?
1294
- @direction == :down
1295
- end
1386
+ def use_transaction?(migration)
1387
+ !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1388
+ end
1296
1389
 
1297
- # Wrap the migration in a transaction only if supported by the adapter.
1298
- def ddl_transaction(migration)
1299
- if use_transaction?(migration)
1300
- Base.transaction { yield }
1301
- else
1302
- yield
1390
+ def use_advisory_lock?
1391
+ Base.connection.advisory_locks_enabled?
1303
1392
  end
1304
- end
1305
1393
 
1306
- def use_transaction?(migration)
1307
- !migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
1308
- end
1394
+ def with_advisory_lock
1395
+ lock_id = generate_migrator_advisory_lock_id
1396
+
1397
+ with_advisory_lock_connection do |connection|
1398
+ got_lock = connection.get_advisory_lock(lock_id)
1399
+ raise ConcurrentMigrationError unless got_lock
1400
+ load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1401
+ yield
1402
+ ensure
1403
+ if got_lock && !connection.release_advisory_lock(lock_id)
1404
+ raise ConcurrentMigrationError.new(
1405
+ ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
1406
+ )
1407
+ end
1408
+ end
1409
+ end
1309
1410
 
1310
- def use_advisory_lock?
1311
- Base.connection.supports_advisory_locks?
1312
- end
1411
+ def with_advisory_lock_connection
1412
+ pool = ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
1413
+ ActiveRecord::Base.connection_db_config
1414
+ )
1313
1415
 
1314
- def with_advisory_lock
1315
- lock_id = generate_migrator_advisory_lock_id
1316
- got_lock = Base.connection.get_advisory_lock(lock_id)
1317
- raise ConcurrentMigrationError unless got_lock
1318
- load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
1319
- yield
1320
- ensure
1321
- Base.connection.release_advisory_lock(lock_id) if got_lock
1322
- end
1416
+ pool.with_connection { |connection| yield(connection) }
1417
+ ensure
1418
+ pool&.disconnect!
1419
+ end
1323
1420
 
1324
- MIGRATOR_SALT = 2053462845
1325
- def generate_migrator_advisory_lock_id
1326
- db_name_hash = Zlib.crc32(Base.connection.current_database)
1327
- MIGRATOR_SALT * db_name_hash
1328
- end
1421
+ MIGRATOR_SALT = 2053462845
1422
+ def generate_migrator_advisory_lock_id
1423
+ db_name_hash = Zlib.crc32(Base.connection.current_database)
1424
+ MIGRATOR_SALT * db_name_hash
1425
+ end
1329
1426
  end
1330
1427
  end