activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -14,9 +14,9 @@ module ActiveRecord
14
14
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
15
15
  #
16
16
  # Account.transaction do
17
- # # select * from accounts where name = 'shugo' limit 1 for update
18
- # shugo = Account.where("name = 'shugo'").lock(true).first
19
- # 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")
20
20
  # shugo.balance -= 100
21
21
  # shugo.save!
22
22
  # yuko.balance += 100
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  class LogSubscriber < ActiveSupport::LogSubscriber
5
5
  IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
6
6
 
7
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
+
7
9
  def self.runtime=(value)
8
10
  ActiveRecord::RuntimeRegistry.sql_runtime = value
9
11
  end
@@ -38,7 +40,7 @@ module ActiveRecord
38
40
  end
39
41
 
40
42
  name = colorize_payload_name(name, payload[:name])
41
- sql = color(sql, sql_color(sql), true)
43
+ sql = color(sql, sql_color(sql), true) if colorize_logging
42
44
 
43
45
  debug " #{name} #{sql}#{binds}"
44
46
  end
@@ -100,36 +102,15 @@ module ActiveRecord
100
102
  end
101
103
 
102
104
  def log_query_source
103
- source_line, line_number = extract_callstack(caller_locations)
104
-
105
- if source_line
106
- if defined?(::Rails.root)
107
- app_root = "#{::Rails.root.to_s}/".freeze
108
- source_line = source_line.sub(app_root, "")
109
- end
110
-
111
- logger.debug(" ↳ #{ source_line }:#{ line_number }")
112
- end
113
- end
105
+ source = extract_query_source_location(caller)
114
106
 
115
- def extract_callstack(callstack)
116
- line = callstack.find do |frame|
117
- frame.absolute_path && !ignored_callstack(frame.absolute_path)
107
+ if source
108
+ logger.debug(" ↳ #{source}")
118
109
  end
119
-
120
- offending_line = line || callstack.first
121
-
122
- [
123
- offending_line.path,
124
- offending_line.lineno
125
- ]
126
110
  end
127
111
 
128
- RAILS_GEM_ROOT = File.expand_path("../../..", __dir__) + "/"
129
-
130
- def ignored_callstack(path)
131
- path.start_with?(RAILS_GEM_ROOT) ||
132
- path.start_with?(RbConfig::CONFIG["rubylibdir"])
112
+ def extract_query_source_location(locations)
113
+ backtrace_cleaner.clean(locations.lazy).first
133
114
  end
134
115
  end
135
116
  end
@@ -0,0 +1,74 @@
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
+ if reading_request?(request)
63
+ resolver.read(&blk)
64
+ else
65
+ resolver.write(&blk)
66
+ end
67
+ end
68
+
69
+ def reading_request?(request)
70
+ request.get? || request.head?
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,87 @@
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
+ def read_from_primary(&blk)
48
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
49
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
50
+ yield
51
+ end
52
+ end
53
+ end
54
+
55
+ def read_from_replica(&blk)
56
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
57
+ instrumenter.instrument("database_selector.active_record.read_from_replica") do
58
+ yield
59
+ end
60
+ end
61
+ end
62
+
63
+ def write_to_primary(&blk)
64
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
65
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
66
+ yield
67
+ ensure
68
+ context.update_last_write_timestamp
69
+ end
70
+ end
71
+ end
72
+
73
+ def read_from_primary?
74
+ !time_since_last_write_ok?
75
+ end
76
+
77
+ def send_to_replica_delay
78
+ delay
79
+ end
80
+
81
+ def time_since_last_write_ok?
82
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
83
+ end
84
+ end
85
+ end
86
+ end
87
+ 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,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "benchmark"
3
4
  require "set"
4
5
  require "zlib"
5
6
  require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
6
8
 
7
9
  module ActiveRecord
8
- class MigrationError < ActiveRecordError#:nodoc:
10
+ class MigrationError < ActiveRecordError #:nodoc:
9
11
  def initialize(message = nil)
10
12
  message = "\n\n#{message}\n\n" if message
11
13
  super
@@ -22,7 +24,7 @@ module ActiveRecord
22
24
  # t.string :zipcode
23
25
  # end
24
26
  #
25
- # execute <<-SQL
27
+ # execute <<~SQL
26
28
  # ALTER TABLE distributors
27
29
  # ADD CONSTRAINT zipchk
28
30
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -40,7 +42,7 @@ module ActiveRecord
40
42
  # t.string :zipcode
41
43
  # end
42
44
  #
43
- # execute <<-SQL
45
+ # execute <<~SQL
44
46
  # ALTER TABLE distributors
45
47
  # ADD CONSTRAINT zipchk
46
48
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -48,7 +50,7 @@ module ActiveRecord
48
50
  # end
49
51
  #
50
52
  # def down
51
- # execute <<-SQL
53
+ # execute <<~SQL
52
54
  # ALTER TABLE distributors
53
55
  # DROP CONSTRAINT zipchk
54
56
  # SQL
@@ -67,7 +69,7 @@ module ActiveRecord
67
69
  #
68
70
  # reversible do |dir|
69
71
  # dir.up do
70
- # execute <<-SQL
72
+ # execute <<~SQL
71
73
  # ALTER TABLE distributors
72
74
  # ADD CONSTRAINT zipchk
73
75
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -75,7 +77,7 @@ module ActiveRecord
75
77
  # end
76
78
  #
77
79
  # dir.down do
78
- # execute <<-SQL
80
+ # execute <<~SQL
79
81
  # ALTER TABLE distributors
80
82
  # DROP CONSTRAINT zipchk
81
83
  # SQL
@@ -86,7 +88,7 @@ module ActiveRecord
86
88
  class IrreversibleMigration < MigrationError
87
89
  end
88
90
 
89
- class DuplicateMigrationVersionError < MigrationError#:nodoc:
91
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
90
92
  def initialize(version = nil)
91
93
  if version
92
94
  super("Multiple migrations have the version number #{version}.")
@@ -96,7 +98,7 @@ module ActiveRecord
96
98
  end
97
99
  end
98
100
 
99
- class DuplicateMigrationNameError < MigrationError#:nodoc:
101
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
100
102
  def initialize(name = nil)
101
103
  if name
102
104
  super("Multiple migrations have the name #{name}.")
@@ -116,7 +118,7 @@ module ActiveRecord
116
118
  end
117
119
  end
118
120
 
119
- class IllegalMigrationNameError < MigrationError#:nodoc:
121
+ class IllegalMigrationNameError < MigrationError #:nodoc:
120
122
  def initialize(name = nil)
121
123
  if name
122
124
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
@@ -126,12 +128,18 @@ module ActiveRecord
126
128
  end
127
129
  end
128
130
 
129
- 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
+
130
138
  def initialize(message = nil)
131
139
  if !message && defined?(Rails.env)
132
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
140
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
133
141
  elsif !message
134
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
142
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate")
135
143
  else
136
144
  super
137
145
  end
@@ -139,8 +147,8 @@ module ActiveRecord
139
147
  end
140
148
 
141
149
  class ConcurrentMigrationError < MigrationError #:nodoc:
142
- DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running.".freeze
143
- RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock".freeze
150
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
151
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
144
152
 
145
153
  def initialize(message = DEFAULT_MESSAGE)
146
154
  super
@@ -149,7 +157,7 @@ module ActiveRecord
149
157
 
150
158
  class NoEnvironmentInSchemaError < MigrationError #:nodoc:
151
159
  def initialize
152
- msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
160
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n rails db:environment:set"
153
161
  if defined?(Rails.env)
154
162
  super("#{msg} RAILS_ENV=#{::Rails.env}")
155
163
  else
@@ -160,7 +168,7 @@ module ActiveRecord
160
168
 
161
169
  class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
162
170
  def initialize(env = "production")
163
- msg = "You are attempting to run a destructive action against your '#{env}' database.\n".dup
171
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
164
172
  msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
165
173
  msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
166
174
  super(msg)
@@ -169,10 +177,10 @@ module ActiveRecord
169
177
 
170
178
  class EnvironmentMismatchError < ActiveRecordError
171
179
  def initialize(current: nil, stored: nil)
172
- msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n".dup
180
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
173
181
  msg << "You are running in `#{ current }` environment. "
174
182
  msg << "If you are sure you want to continue, first set the environment using:\n\n"
175
- msg << " bin/rails db:environment:set"
183
+ msg << " rails db:environment:set"
176
184
  if defined?(Rails.env)
177
185
  super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
178
186
  else
@@ -307,7 +315,7 @@ module ActiveRecord
307
315
  # named +column_name+ from the table called +table_name+.
308
316
  # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
309
317
  # columns from the table definition.
310
- # * <tt>remove_foreign_key(from_table, options_or_to_table)</tt>: Removes the
318
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
311
319
  # given foreign key from the table called +table_name+.
312
320
  # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
313
321
  # specified by +column_names+.
@@ -351,7 +359,7 @@ module ActiveRecord
351
359
  # <tt>rails db:migrate</tt>. This will update the database by running all of the
352
360
  # pending migrations, creating the <tt>schema_migrations</tt> table
353
361
  # (see "About the schema_migrations table" section below) if missing. It will also
354
- # 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
355
363
  # to match the structure of your database.
356
364
  #
357
365
  # To roll the database back to a previous migration version, use
@@ -486,9 +494,9 @@ module ActiveRecord
486
494
  # This migration will create the horses table for you on the way up, and
487
495
  # automatically figure out how to drop the table on the way down.
488
496
  #
489
- # Some commands like +remove_column+ cannot be reversed. If you care to
490
- # define how to move up and down in these cases, you should define the +up+
491
- # 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.
492
500
  #
493
501
  # If a command cannot be reversed, an
494
502
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -519,10 +527,10 @@ module ActiveRecord
519
527
  autoload :Compatibility, "active_record/migration/compatibility"
520
528
 
521
529
  # This must be defined before the inherited hook, below
522
- class Current < Migration # :nodoc:
530
+ class Current < Migration #:nodoc:
523
531
  end
524
532
 
525
- def self.inherited(subclass) # :nodoc:
533
+ def self.inherited(subclass) #:nodoc:
526
534
  super
527
535
  if subclass.superclass == Migration
528
536
  raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
@@ -540,7 +548,7 @@ module ActiveRecord
540
548
  ActiveRecord::VERSION::STRING.to_f
541
549
  end
542
550
 
543
- MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
551
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
544
552
 
545
553
  # This class is used to verify that all migrations have been run before
546
554
  # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
@@ -560,17 +568,16 @@ module ActiveRecord
560
568
  end
561
569
 
562
570
  private
563
-
564
571
  def connection
565
572
  ActiveRecord::Base.connection
566
573
  end
567
574
  end
568
575
 
569
576
  class << self
570
- attr_accessor :delegate # :nodoc:
571
- attr_accessor :disable_ddl_transaction # :nodoc:
577
+ attr_accessor :delegate #:nodoc:
578
+ attr_accessor :disable_ddl_transaction #:nodoc:
572
579
 
573
- def nearest_delegate # :nodoc:
580
+ def nearest_delegate #:nodoc:
574
581
  delegate || superclass.nearest_delegate
575
582
  end
576
583
 
@@ -580,29 +587,38 @@ module ActiveRecord
580
587
  end
581
588
 
582
589
  def load_schema_if_pending!
583
- if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
590
+ current_config = Base.connection_config
591
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
592
+
593
+ needs_update = !all_configs.all? do |db_config|
594
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
595
+ end
596
+
597
+ if needs_update
584
598
  # Roundtrip to Rake to allow plugins to hook into database initialization.
585
599
  root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
586
600
  FileUtils.cd(root) do
587
- current_config = Base.connection_config
588
601
  Base.clear_all_connections!
589
602
  system("bin/rails db:test:prepare")
590
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
591
- Base.establish_connection(current_config)
592
603
  end
593
- check_pending!
594
604
  end
605
+
606
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
607
+ Base.establish_connection(current_config)
608
+
609
+ check_pending!
595
610
  end
596
611
 
597
- def maintain_test_schema! # :nodoc:
612
+ def maintain_test_schema! #:nodoc:
598
613
  if ActiveRecord::Base.maintain_test_schema
599
614
  suppress_messages { load_schema_if_pending! }
600
615
  end
601
616
  end
602
617
 
603
- def method_missing(name, *args, &block) # :nodoc:
618
+ def method_missing(name, *args, &block) #:nodoc:
604
619
  nearest_delegate.send(name, *args, &block)
605
620
  end
621
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
606
622
 
607
623
  def migrate(direction)
608
624
  new.migrate direction
@@ -617,7 +633,7 @@ module ActiveRecord
617
633
  end
618
634
  end
619
635
 
620
- def disable_ddl_transaction # :nodoc:
636
+ def disable_ddl_transaction #:nodoc:
621
637
  self.class.disable_ddl_transaction
622
638
  end
623
639
 
@@ -677,15 +693,13 @@ module ActiveRecord
677
693
  if connection.respond_to? :revert
678
694
  connection.revert { yield }
679
695
  else
680
- recorder = CommandRecorder.new(connection)
696
+ recorder = command_recorder
681
697
  @connection = recorder
682
698
  suppress_messages do
683
699
  connection.revert { yield }
684
700
  end
685
701
  @connection = recorder.delegate
686
- recorder.commands.each do |cmd, args, block|
687
- send(cmd, *args, &block)
688
- end
702
+ recorder.replay(self)
689
703
  end
690
704
  end
691
705
  end
@@ -694,7 +708,7 @@ module ActiveRecord
694
708
  connection.respond_to?(:reverting) && connection.reverting
695
709
  end
696
710
 
697
- ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc:
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
698
712
  def up
699
713
  yield unless reverting
700
714
  end
@@ -830,10 +844,14 @@ module ActiveRecord
830
844
  write "== %s %s" % [text, "=" * length]
831
845
  end
832
846
 
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.
833
849
  def say(message, subitem = false)
834
850
  write "#{subitem ? " ->" : "--"} #{message}"
835
851
  end
836
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.
837
855
  def say_with_time(message)
838
856
  say(message)
839
857
  result = nil
@@ -843,6 +861,7 @@ module ActiveRecord
843
861
  result
844
862
  end
845
863
 
864
+ # Takes a block as an argument and suppresses any output generated by the block.
846
865
  def suppress_messages
847
866
  save, self.verbose = verbose, false
848
867
  yield
@@ -871,21 +890,23 @@ module ActiveRecord
871
890
  connection.send(method, *arguments, &block)
872
891
  end
873
892
  end
893
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
874
894
 
875
895
  def copy(destination, sources, options = {})
876
896
  copied = []
897
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
877
898
 
878
899
  FileUtils.mkdir_p(destination) unless File.exist?(destination)
879
900
 
880
- destination_migrations = ActiveRecord::MigrationContext.new(destination).migrations
901
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
881
902
  last = destination_migrations.last
882
903
  sources.each do |scope, path|
883
- source_migrations = ActiveRecord::MigrationContext.new(path).migrations
904
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
884
905
 
885
906
  source_migrations.each do |migration|
886
907
  source = File.binread(migration.filename)
887
908
  inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
888
- magic_comments = "".dup
909
+ magic_comments = +""
889
910
  loop do
890
911
  # If we have a magic comment in the original migration,
891
912
  # insert our comment after the first newline(end of the magic comment line)
@@ -956,6 +977,10 @@ module ActiveRecord
956
977
  yield
957
978
  end
958
979
  end
980
+
981
+ def command_recorder
982
+ CommandRecorder.new(connection)
983
+ end
959
984
  end
960
985
 
961
986
  # MigrationProxy is used to defer loading of the actual migration classes
@@ -977,7 +1002,6 @@ module ActiveRecord
977
1002
  delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
978
1003
 
979
1004
  private
980
-
981
1005
  def migration
982
1006
  @migration ||= load_migration
983
1007
  end
@@ -998,11 +1022,12 @@ module ActiveRecord
998
1022
  end
999
1023
  end
1000
1024
 
1001
- class MigrationContext # :nodoc:
1002
- attr_reader :migrations_paths
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
1003
1027
 
1004
- def initialize(migrations_paths)
1028
+ def initialize(migrations_paths, schema_migration)
1005
1029
  @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1006
1031
  end
1007
1032
 
1008
1033
  def migrate(target_version = nil, &block)
@@ -1033,7 +1058,7 @@ module ActiveRecord
1033
1058
  migrations
1034
1059
  end
1035
1060
 
1036
- Migrator.new(:up, selected_migrations, target_version).migrate
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1037
1062
  end
1038
1063
 
1039
1064
  def down(target_version = nil)
@@ -1043,20 +1068,20 @@ module ActiveRecord
1043
1068
  migrations
1044
1069
  end
1045
1070
 
1046
- Migrator.new(:down, selected_migrations, target_version).migrate
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1047
1072
  end
1048
1073
 
1049
1074
  def run(direction, target_version)
1050
- Migrator.new(direction, migrations, target_version).run
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1051
1076
  end
1052
1077
 
1053
1078
  def open
1054
- Migrator.new(:up, migrations, nil)
1079
+ Migrator.new(:up, migrations, schema_migration)
1055
1080
  end
1056
1081
 
1057
1082
  def get_all_versions
1058
- if SchemaMigration.table_exists?
1059
- SchemaMigration.all_versions.map(&:to_i)
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1060
1085
  else
1061
1086
  []
1062
1087
  end
@@ -1079,10 +1104,6 @@ module ActiveRecord
1079
1104
  migrations.last || NullMigration.new
1080
1105
  end
1081
1106
 
1082
- def parse_migration_filename(filename) # :nodoc:
1083
- File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1084
- end
1085
-
1086
1107
  def migrations
1087
1108
  migrations = migration_files.map do |file|
1088
1109
  version, name, scope = parse_migration_filename(file)
@@ -1097,12 +1118,12 @@ module ActiveRecord
1097
1118
  end
1098
1119
 
1099
1120
  def migrations_status
1100
- db_list = ActiveRecord::SchemaMigration.normalized_versions
1121
+ db_list = schema_migration.normalized_versions
1101
1122
 
1102
1123
  file_list = migration_files.map do |file|
1103
1124
  version, name, scope = parse_migration_filename(file)
1104
1125
  raise IllegalMigrationNameError.new(file) unless version
1105
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
1126
+ version = schema_migration.normalize_migration_number(version)
1106
1127
  status = db_list.delete(version) ? "up" : "down"
1107
1128
  [status, version, (name + scope).humanize]
1108
1129
  end.compact
@@ -1114,11 +1135,6 @@ module ActiveRecord
1114
1135
  (db_list + file_list).sort_by { |_, version, _| version }
1115
1136
  end
1116
1137
 
1117
- def migration_files
1118
- paths = Array(migrations_paths)
1119
- Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1120
- end
1121
-
1122
1138
  def current_environment
1123
1139
  ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1124
1140
  end
@@ -1137,8 +1153,17 @@ module ActiveRecord
1137
1153
  end
1138
1154
 
1139
1155
  private
1156
+ def migration_files
1157
+ paths = Array(migrations_paths)
1158
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1159
+ end
1160
+
1161
+ def parse_migration_filename(filename)
1162
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1163
+ end
1164
+
1140
1165
  def move(direction, steps)
1141
- migrator = Migrator.new(direction, migrations)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
1142
1167
 
1143
1168
  if current_version != 0 && !migrator.current_migration
1144
1169
  raise UnknownMigrationVersionError.new(current_version)
@@ -1161,30 +1186,24 @@ module ActiveRecord
1161
1186
  class << self
1162
1187
  attr_accessor :migrations_paths
1163
1188
 
1164
- def migrations_path=(path)
1165
- ActiveSupport::Deprecation.warn \
1166
- "`ActiveRecord::Migrator.migrations_path=` is now deprecated and will be removed in Rails 6.0. " \
1167
- "You can set the `migrations_paths` on the `connection` instead through the `database.yml`."
1168
- self.migrations_paths = [path]
1169
- end
1170
-
1171
1189
  # For cases where a table doesn't exist like loading from schema cache
1172
1190
  def current_version
1173
- MigrationContext.new(migrations_paths).current_version
1191
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
1174
1192
  end
1175
1193
  end
1176
1194
 
1177
1195
  self.migrations_paths = ["db/migrate"]
1178
1196
 
1179
- def initialize(direction, migrations, target_version = nil)
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1180
1198
  @direction = direction
1181
1199
  @target_version = target_version
1182
1200
  @migrated_versions = nil
1183
1201
  @migrations = migrations
1202
+ @schema_migration = schema_migration
1184
1203
 
1185
1204
  validate(@migrations)
1186
1205
 
1187
- ActiveRecord::SchemaMigration.create_table
1206
+ @schema_migration.create_table
1188
1207
  ActiveRecord::InternalMetadata.create_table
1189
1208
  end
1190
1209
 
@@ -1238,11 +1257,10 @@ module ActiveRecord
1238
1257
  end
1239
1258
 
1240
1259
  def load_migrated
1241
- @migrated_versions = Set.new(Base.connection.migration_context.get_all_versions)
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1242
1261
  end
1243
1262
 
1244
1263
  private
1245
-
1246
1264
  # Used for running a specific migration.
1247
1265
  def run_without_lock
1248
1266
  migration = migrations.detect { |m| m.version == @target_version }
@@ -1293,7 +1311,7 @@ module ActiveRecord
1293
1311
  record_version_state_after_migrating(migration.version)
1294
1312
  end
1295
1313
  rescue => e
1296
- msg = "An error has occurred, ".dup
1314
+ msg = +"An error has occurred, "
1297
1315
  msg << "this and " if use_transaction?(migration)
1298
1316
  msg << "all later migrations canceled:\n\n#{e}"
1299
1317
  raise StandardError, msg, e.backtrace
@@ -1322,10 +1340,10 @@ module ActiveRecord
1322
1340
  def record_version_state_after_migrating(version)
1323
1341
  if down?
1324
1342
  migrated.delete(version)
1325
- ActiveRecord::SchemaMigration.where(version: version.to_s).delete_all
1343
+ @schema_migration.delete_by(version: version.to_s)
1326
1344
  else
1327
1345
  migrated << version
1328
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
1346
+ @schema_migration.create!(version: version.to_s)
1329
1347
  end
1330
1348
  end
1331
1349
 
@@ -1351,12 +1369,13 @@ module ActiveRecord
1351
1369
  end
1352
1370
 
1353
1371
  def use_advisory_lock?
1354
- Base.connection.supports_advisory_locks?
1372
+ Base.connection.advisory_locks_enabled?
1355
1373
  end
1356
1374
 
1357
1375
  def with_advisory_lock
1358
1376
  lock_id = generate_migrator_advisory_lock_id
1359
- connection = Base.connection
1377
+ AdvisoryLockBase.establish_connection(ActiveRecord::Base.connection_config) unless AdvisoryLockBase.connected?
1378
+ connection = AdvisoryLockBase.connection
1360
1379
  got_lock = connection.get_advisory_lock(lock_id)
1361
1380
  raise ConcurrentMigrationError unless got_lock
1362
1381
  load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock