activerecord 6.0.0.beta1 → 6.0.1.rc1

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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +529 -10
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +7 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +27 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +5 -6
  16. data/lib/active_record/associations/collection_proxy.rb +13 -42
  17. data/lib/active_record/associations/has_many_association.rb +1 -9
  18. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  19. data/lib/active_record/associations/join_dependency.rb +14 -9
  20. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  21. data/lib/active_record/associations/preloader.rb +12 -7
  22. data/lib/active_record/associations/preloader/association.rb +37 -34
  23. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  24. data/lib/active_record/attribute_methods.rb +3 -53
  25. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  26. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  27. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  28. data/lib/active_record/attribute_methods/query.rb +2 -3
  29. data/lib/active_record/attribute_methods/read.rb +3 -9
  30. data/lib/active_record/attribute_methods/write.rb +6 -12
  31. data/lib/active_record/attributes.rb +13 -0
  32. data/lib/active_record/autosave_association.rb +21 -7
  33. data/lib/active_record/base.rb +0 -1
  34. data/lib/active_record/callbacks.rb +3 -3
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
  36. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  37. data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
  38. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
  39. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  40. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  41. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  42. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  43. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  44. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  45. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
  46. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
  47. data/lib/active_record/connection_adapters/column.rb +17 -13
  48. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  49. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  50. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  51. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  52. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  53. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  54. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  55. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  56. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  59. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  60. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  61. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  62. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  63. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  66. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
  68. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  69. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  70. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  71. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  72. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  73. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
  74. data/lib/active_record/connection_handling.rb +40 -17
  75. data/lib/active_record/core.rb +35 -24
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  78. data/lib/active_record/database_configurations/url_config.rb +21 -16
  79. data/lib/active_record/dynamic_matchers.rb +1 -1
  80. data/lib/active_record/enum.rb +15 -0
  81. data/lib/active_record/errors.rb +18 -13
  82. data/lib/active_record/fixtures.rb +11 -6
  83. data/lib/active_record/gem_version.rb +2 -2
  84. data/lib/active_record/inheritance.rb +1 -1
  85. data/lib/active_record/insert_all.rb +179 -0
  86. data/lib/active_record/integration.rb +13 -1
  87. data/lib/active_record/internal_metadata.rb +5 -1
  88. data/lib/active_record/locking/optimistic.rb +3 -4
  89. data/lib/active_record/log_subscriber.rb +1 -1
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  92. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/migration/command_recorder.rb +28 -14
  95. data/lib/active_record/migration/compatibility.rb +72 -63
  96. data/lib/active_record/model_schema.rb +3 -0
  97. data/lib/active_record/persistence.rb +212 -19
  98. data/lib/active_record/querying.rb +18 -14
  99. data/lib/active_record/railtie.rb +9 -1
  100. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  101. data/lib/active_record/railties/databases.rake +124 -25
  102. data/lib/active_record/reflection.rb +18 -32
  103. data/lib/active_record/relation.rb +185 -35
  104. data/lib/active_record/relation/calculations.rb +40 -44
  105. data/lib/active_record/relation/delegation.rb +23 -31
  106. data/lib/active_record/relation/finder_methods.rb +23 -14
  107. data/lib/active_record/relation/merger.rb +11 -16
  108. data/lib/active_record/relation/query_attribute.rb +5 -3
  109. data/lib/active_record/relation/query_methods.rb +230 -69
  110. data/lib/active_record/relation/spawn_methods.rb +1 -1
  111. data/lib/active_record/relation/where_clause.rb +10 -10
  112. data/lib/active_record/sanitization.rb +33 -4
  113. data/lib/active_record/schema.rb +1 -1
  114. data/lib/active_record/schema_dumper.rb +10 -1
  115. data/lib/active_record/schema_migration.rb +1 -1
  116. data/lib/active_record/scoping.rb +6 -7
  117. data/lib/active_record/scoping/default.rb +7 -15
  118. data/lib/active_record/scoping/named.rb +10 -2
  119. data/lib/active_record/statement_cache.rb +2 -2
  120. data/lib/active_record/store.rb +48 -0
  121. data/lib/active_record/table_metadata.rb +9 -13
  122. data/lib/active_record/tasks/database_tasks.rb +109 -6
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  124. data/lib/active_record/test_databases.rb +1 -16
  125. data/lib/active_record/test_fixtures.rb +2 -2
  126. data/lib/active_record/timestamp.rb +35 -19
  127. data/lib/active_record/touch_later.rb +4 -2
  128. data/lib/active_record/transactions.rb +56 -46
  129. data/lib/active_record/type_caster/connection.rb +16 -10
  130. data/lib/active_record/validations.rb +1 -0
  131. data/lib/active_record/validations/uniqueness.rb +4 -4
  132. data/lib/arel.rb +18 -4
  133. data/lib/arel/insert_manager.rb +3 -3
  134. data/lib/arel/nodes.rb +2 -1
  135. data/lib/arel/nodes/and.rb +1 -1
  136. data/lib/arel/nodes/case.rb +1 -1
  137. data/lib/arel/nodes/comment.rb +29 -0
  138. data/lib/arel/nodes/select_core.rb +16 -12
  139. data/lib/arel/nodes/unary.rb +1 -0
  140. data/lib/arel/nodes/values_list.rb +2 -17
  141. data/lib/arel/select_manager.rb +10 -10
  142. data/lib/arel/visitors/depth_first.rb +7 -2
  143. data/lib/arel/visitors/dot.rb +7 -2
  144. data/lib/arel/visitors/ibm_db.rb +13 -0
  145. data/lib/arel/visitors/informix.rb +6 -0
  146. data/lib/arel/visitors/mssql.rb +15 -1
  147. data/lib/arel/visitors/oracle12.rb +4 -5
  148. data/lib/arel/visitors/postgresql.rb +4 -10
  149. data/lib/arel/visitors/to_sql.rb +107 -131
  150. data/lib/arel/visitors/visitor.rb +9 -5
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  152. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  154. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  155. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  156. metadata +19 -12
  157. data/lib/active_record/collection_cache_key.rb +0 -53
  158. data/lib/arel/nodes/values.rb +0 -16
@@ -22,6 +22,14 @@ module ActiveRecord
22
22
  #
23
23
  # This is +true+, by default on Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Indicates whether to use a stable #cache_key method that is accompanied
29
+ # by a changing version in the #cache_version method on collections.
30
+ #
31
+ # This is +false+, by default until Rails 6.1.
32
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
25
33
  end
26
34
 
27
35
  # Returns a +String+, which Action Pack uses for constructing a URL to this
@@ -152,6 +160,10 @@ module ActiveRecord
152
160
  end
153
161
  end
154
162
  end
163
+
164
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
165
+ collection.send(:compute_cache_key, timestamp_column)
166
+ end
155
167
  end
156
168
 
157
169
  private
@@ -180,7 +192,7 @@ module ActiveRecord
180
192
  # raw_timestamp_to_cache_version(timestamp)
181
193
  # # => "20181015200215266505"
182
194
  #
183
- # Postgres truncates trailing zeros,
195
+ # PostgreSQL truncates trailing zeros,
184
196
  # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
185
197
  # to account for this we pad the output with zeros
186
198
  def raw_timestamp_to_cache_version(timestamp)
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  def table_name
20
- "#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
20
+ "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
21
21
  end
22
22
 
23
23
  def []=(key, value)
@@ -44,6 +44,10 @@ module ActiveRecord
44
44
  end
45
45
  end
46
46
  end
47
+
48
+ def drop_table
49
+ connection.drop_table table_name, if_exists: true
50
+ end
47
51
  end
48
52
  end
49
53
  end
@@ -71,9 +71,8 @@ module ActiveRecord
71
71
  end
72
72
 
73
73
  def _touch_row(attribute_names, time)
74
+ @_touch_attr_names << self.class.locking_column if locking_enabled?
74
75
  super
75
- ensure
76
- clear_attribute_change(self.class.locking_column) if locking_enabled?
77
76
  end
78
77
 
79
78
  def _update_row(attribute_names, attempted_action = "update")
@@ -88,7 +87,7 @@ module ActiveRecord
88
87
 
89
88
  affected_rows = self.class._update_record(
90
89
  attributes_with_values(attribute_names),
91
- self.class.primary_key => id_in_database,
90
+ @primary_key => id_in_database,
92
91
  locking_column => previous_lock_value
93
92
  )
94
93
 
@@ -111,7 +110,7 @@ module ActiveRecord
111
110
  locking_column = self.class.locking_column
112
111
 
113
112
  affected_rows = self.class._delete_record(
114
- self.class.primary_key => id_in_database,
113
+ @primary_key => id_in_database,
115
114
  locking_column => read_attribute_before_type_cast(locking_column)
116
115
  )
117
116
 
@@ -110,7 +110,7 @@ module ActiveRecord
110
110
  end
111
111
 
112
112
  def extract_query_source_location(locations)
113
- backtrace_cleaner.clean(locations).first
113
+ backtrace_cleaner.clean(locations.lazy).first
114
114
  end
115
115
  end
116
116
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ # The DatabaseSelector Middleware provides a framework for automatically
8
+ # swapping from the primary to the replica database connection. Rails
9
+ # provides a basic framework to determine when to swap and allows for
10
+ # applications to write custom strategy classes to override the default
11
+ # behavior.
12
+ #
13
+ # The resolver class defines when the application should switch (i.e. read
14
+ # from the primary if a write occurred less than 2 seconds ago) and a
15
+ # resolver context class that sets a value that helps the resolver class
16
+ # decide when to switch.
17
+ #
18
+ # Rails default middleware uses the request's session to set a timestamp
19
+ # that informs the application when to read from a primary or read from a
20
+ # replica.
21
+ #
22
+ # To use the DatabaseSelector in your application with default settings add
23
+ # the following options to your environment config:
24
+ #
25
+ # config.active_record.database_selector = { delay: 2.seconds }
26
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
27
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
28
+ #
29
+ # New applications will include these lines commented out in the production.rb.
30
+ #
31
+ # The default behavior can be changed by setting the config options to a
32
+ # custom class:
33
+ #
34
+ # config.active_record.database_selector = { delay: 2.seconds }
35
+ # config.active_record.database_resolver = MyResolver
36
+ # config.active_record.database_resolver_context = MyResolver::MySession
37
+ class DatabaseSelector
38
+ def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
39
+ @app = app
40
+ @resolver_klass = resolver_klass || Resolver
41
+ @context_klass = context_klass || Resolver::Session
42
+ @options = options
43
+ end
44
+
45
+ attr_reader :resolver_klass, :context_klass, :options
46
+
47
+ # Middleware that determines which database connection to use in a multiple
48
+ # database application.
49
+ def call(env)
50
+ request = ActionDispatch::Request.new(env)
51
+
52
+ select_database(request) do
53
+ @app.call(env)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def select_database(request, &blk)
60
+ context = context_klass.call(request)
61
+ resolver = resolver_klass.call(context, options)
62
+
63
+ if reading_request?(request)
64
+ resolver.read(&blk)
65
+ else
66
+ resolver.write(&blk)
67
+ end
68
+ end
69
+
70
+ def reading_request?(request)
71
+ request.get? || request.head?
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/middleware/database_selector/resolver/session"
4
+
5
+ module ActiveRecord
6
+ module Middleware
7
+ class DatabaseSelector
8
+ # The Resolver class is used by the DatabaseSelector middleware to
9
+ # determine which database the request should use.
10
+ #
11
+ # To change the behavior of the Resolver class in your application,
12
+ # create a custom resolver class that inherits from
13
+ # DatabaseSelector::Resolver and implements the methods that need to
14
+ # be changed.
15
+ #
16
+ # By default the Resolver class will send read traffic to the replica
17
+ # if it's been 2 seconds since the last write.
18
+ class Resolver # :nodoc:
19
+ SEND_TO_REPLICA_DELAY = 2.seconds
20
+
21
+ def self.call(context, options = {})
22
+ new(context, options)
23
+ end
24
+
25
+ def initialize(context, options = {})
26
+ @context = context
27
+ @options = options
28
+ @delay = @options && @options[:delay] ? @options[:delay] : SEND_TO_REPLICA_DELAY
29
+ @instrumenter = ActiveSupport::Notifications.instrumenter
30
+ end
31
+
32
+ attr_reader :context, :delay, :instrumenter
33
+
34
+ def read(&blk)
35
+ if read_from_primary?
36
+ read_from_primary(&blk)
37
+ else
38
+ read_from_replica(&blk)
39
+ end
40
+ end
41
+
42
+ def write(&blk)
43
+ write_to_primary(&blk)
44
+ end
45
+
46
+ private
47
+
48
+ def read_from_primary(&blk)
49
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
50
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
51
+ yield
52
+ end
53
+ end
54
+ end
55
+
56
+ def read_from_replica(&blk)
57
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
58
+ instrumenter.instrument("database_selector.active_record.read_from_replica") do
59
+ yield
60
+ end
61
+ end
62
+ end
63
+
64
+ def write_to_primary(&blk)
65
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
66
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
67
+ yield
68
+ ensure
69
+ context.update_last_write_timestamp
70
+ end
71
+ end
72
+ end
73
+
74
+ def read_from_primary?
75
+ !time_since_last_write_ok?
76
+ end
77
+
78
+ def send_to_replica_delay
79
+ delay
80
+ end
81
+
82
+ def time_since_last_write_ok?
83
+ Time.now - context.last_write_timestamp >= send_to_replica_delay
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Middleware
5
+ class DatabaseSelector
6
+ class Resolver
7
+ # The session class is used by the DatabaseSelector::Resolver to save
8
+ # timestamps of the last write in the session.
9
+ #
10
+ # The last_write is used to determine whether it's safe to read
11
+ # from the replica or the request needs to be sent to the primary.
12
+ class Session # :nodoc:
13
+ def self.call(request)
14
+ new(request.session)
15
+ end
16
+
17
+ # Converts time to a timestamp that represents milliseconds since
18
+ # epoch.
19
+ def self.convert_time_to_timestamp(time)
20
+ time.to_i * 1000 + time.usec / 1000
21
+ end
22
+
23
+ # Converts milliseconds since epoch timestamp into a time object.
24
+ def self.convert_timestamp_to_time(timestamp)
25
+ timestamp ? Time.at(timestamp / 1000, (timestamp % 1000) * 1000) : Time.at(0)
26
+ end
27
+
28
+ def initialize(session)
29
+ @session = session
30
+ end
31
+
32
+ attr_reader :session
33
+
34
+ def last_write_timestamp
35
+ self.class.convert_timestamp_to_time(session[:last_write])
36
+ end
37
+
38
+ def update_last_write_timestamp
39
+ session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -4,9 +4,10 @@ require "benchmark"
4
4
  require "set"
5
5
  require "zlib"
6
6
  require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
7
8
 
8
9
  module ActiveRecord
9
- class MigrationError < ActiveRecordError#:nodoc:
10
+ class MigrationError < ActiveRecordError #:nodoc:
10
11
  def initialize(message = nil)
11
12
  message = "\n\n#{message}\n\n" if message
12
13
  super
@@ -87,7 +88,7 @@ module ActiveRecord
87
88
  class IrreversibleMigration < MigrationError
88
89
  end
89
90
 
90
- class DuplicateMigrationVersionError < MigrationError#:nodoc:
91
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
91
92
  def initialize(version = nil)
92
93
  if version
93
94
  super("Multiple migrations have the version number #{version}.")
@@ -97,7 +98,7 @@ module ActiveRecord
97
98
  end
98
99
  end
99
100
 
100
- class DuplicateMigrationNameError < MigrationError#:nodoc:
101
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
101
102
  def initialize(name = nil)
102
103
  if name
103
104
  super("Multiple migrations have the name #{name}.")
@@ -117,7 +118,7 @@ module ActiveRecord
117
118
  end
118
119
  end
119
120
 
120
- class IllegalMigrationNameError < MigrationError#:nodoc:
121
+ class IllegalMigrationNameError < MigrationError #:nodoc:
121
122
  def initialize(name = nil)
122
123
  if name
123
124
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
@@ -127,7 +128,13 @@ module ActiveRecord
127
128
  end
128
129
  end
129
130
 
130
- 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
+
131
138
  def initialize(message = nil)
132
139
  if !message && defined?(Rails.env)
133
140
  super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
@@ -308,7 +315,7 @@ module ActiveRecord
308
315
  # named +column_name+ from the table called +table_name+.
309
316
  # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
310
317
  # columns from the table definition.
311
- # * <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
312
319
  # given foreign key from the table called +table_name+.
313
320
  # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
314
321
  # specified by +column_names+.
@@ -487,9 +494,9 @@ module ActiveRecord
487
494
  # This migration will create the horses table for you on the way up, and
488
495
  # automatically figure out how to drop the table on the way down.
489
496
  #
490
- # Some commands like +remove_column+ cannot be reversed. If you care to
491
- # define how to move up and down in these cases, you should define the +up+
492
- # 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.
493
500
  #
494
501
  # If a command cannot be reversed, an
495
502
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -520,10 +527,10 @@ module ActiveRecord
520
527
  autoload :Compatibility, "active_record/migration/compatibility"
521
528
 
522
529
  # This must be defined before the inherited hook, below
523
- class Current < Migration # :nodoc:
530
+ class Current < Migration #:nodoc:
524
531
  end
525
532
 
526
- def self.inherited(subclass) # :nodoc:
533
+ def self.inherited(subclass) #:nodoc:
527
534
  super
528
535
  if subclass.superclass == Migration
529
536
  raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
@@ -541,7 +548,7 @@ module ActiveRecord
541
548
  ActiveRecord::VERSION::STRING.to_f
542
549
  end
543
550
 
544
- 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:
545
552
 
546
553
  # This class is used to verify that all migrations have been run before
547
554
  # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
@@ -568,10 +575,10 @@ module ActiveRecord
568
575
  end
569
576
 
570
577
  class << self
571
- attr_accessor :delegate # :nodoc:
572
- attr_accessor :disable_ddl_transaction # :nodoc:
578
+ attr_accessor :delegate #:nodoc:
579
+ attr_accessor :disable_ddl_transaction #:nodoc:
573
580
 
574
- def nearest_delegate # :nodoc:
581
+ def nearest_delegate #:nodoc:
575
582
  delegate || superclass.nearest_delegate
576
583
  end
577
584
 
@@ -581,27 +588,35 @@ module ActiveRecord
581
588
  end
582
589
 
583
590
  def load_schema_if_pending!
584
- if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
591
+ current_config = Base.connection_config
592
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
593
+
594
+ needs_update = !all_configs.all? do |db_config|
595
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
596
+ end
597
+
598
+ if needs_update
585
599
  # Roundtrip to Rake to allow plugins to hook into database initialization.
586
600
  root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
587
601
  FileUtils.cd(root) do
588
- current_config = Base.connection_config
589
602
  Base.clear_all_connections!
590
603
  system("bin/rails db:test:prepare")
591
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
592
- Base.establish_connection(current_config)
593
604
  end
594
- check_pending!
595
605
  end
606
+
607
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
608
+ Base.establish_connection(current_config)
609
+
610
+ check_pending!
596
611
  end
597
612
 
598
- def maintain_test_schema! # :nodoc:
613
+ def maintain_test_schema! #:nodoc:
599
614
  if ActiveRecord::Base.maintain_test_schema
600
615
  suppress_messages { load_schema_if_pending! }
601
616
  end
602
617
  end
603
618
 
604
- def method_missing(name, *args, &block) # :nodoc:
619
+ def method_missing(name, *args, &block) #:nodoc:
605
620
  nearest_delegate.send(name, *args, &block)
606
621
  end
607
622
 
@@ -618,7 +633,7 @@ module ActiveRecord
618
633
  end
619
634
  end
620
635
 
621
- def disable_ddl_transaction # :nodoc:
636
+ def disable_ddl_transaction #:nodoc:
622
637
  self.class.disable_ddl_transaction
623
638
  end
624
639
 
@@ -693,7 +708,7 @@ module ActiveRecord
693
708
  connection.respond_to?(:reverting) && connection.reverting
694
709
  end
695
710
 
696
- ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc:
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
697
712
  def up
698
713
  yield unless reverting
699
714
  end
@@ -878,13 +893,14 @@ module ActiveRecord
878
893
 
879
894
  def copy(destination, sources, options = {})
880
895
  copied = []
896
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
881
897
 
882
898
  FileUtils.mkdir_p(destination) unless File.exist?(destination)
883
899
 
884
- destination_migrations = ActiveRecord::MigrationContext.new(destination).migrations
900
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
885
901
  last = destination_migrations.last
886
902
  sources.each do |scope, path|
887
- source_migrations = ActiveRecord::MigrationContext.new(path).migrations
903
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
888
904
 
889
905
  source_migrations.each do |migration|
890
906
  source = File.binread(migration.filename)
@@ -1006,11 +1022,12 @@ module ActiveRecord
1006
1022
  end
1007
1023
  end
1008
1024
 
1009
- class MigrationContext # :nodoc:
1010
- attr_reader :migrations_paths
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
1011
1027
 
1012
- def initialize(migrations_paths)
1028
+ def initialize(migrations_paths, schema_migration)
1013
1029
  @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1014
1031
  end
1015
1032
 
1016
1033
  def migrate(target_version = nil, &block)
@@ -1041,7 +1058,7 @@ module ActiveRecord
1041
1058
  migrations
1042
1059
  end
1043
1060
 
1044
- Migrator.new(:up, selected_migrations, target_version).migrate
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1045
1062
  end
1046
1063
 
1047
1064
  def down(target_version = nil)
@@ -1051,20 +1068,20 @@ module ActiveRecord
1051
1068
  migrations
1052
1069
  end
1053
1070
 
1054
- Migrator.new(:down, selected_migrations, target_version).migrate
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1055
1072
  end
1056
1073
 
1057
1074
  def run(direction, target_version)
1058
- Migrator.new(direction, migrations, target_version).run
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1059
1076
  end
1060
1077
 
1061
1078
  def open
1062
- Migrator.new(:up, migrations, nil)
1079
+ Migrator.new(:up, migrations, schema_migration)
1063
1080
  end
1064
1081
 
1065
1082
  def get_all_versions
1066
- if SchemaMigration.table_exists?
1067
- SchemaMigration.all_versions.map(&:to_i)
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1068
1085
  else
1069
1086
  []
1070
1087
  end
@@ -1101,12 +1118,12 @@ module ActiveRecord
1101
1118
  end
1102
1119
 
1103
1120
  def migrations_status
1104
- db_list = ActiveRecord::SchemaMigration.normalized_versions
1121
+ db_list = schema_migration.normalized_versions
1105
1122
 
1106
1123
  file_list = migration_files.map do |file|
1107
1124
  version, name, scope = parse_migration_filename(file)
1108
1125
  raise IllegalMigrationNameError.new(file) unless version
1109
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
1126
+ version = schema_migration.normalize_migration_number(version)
1110
1127
  status = db_list.delete(version) ? "up" : "down"
1111
1128
  [status, version, (name + scope).humanize]
1112
1129
  end.compact
@@ -1146,7 +1163,7 @@ module ActiveRecord
1146
1163
  end
1147
1164
 
1148
1165
  def move(direction, steps)
1149
- migrator = Migrator.new(direction, migrations)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
1150
1167
 
1151
1168
  if current_version != 0 && !migrator.current_migration
1152
1169
  raise UnknownMigrationVersionError.new(current_version)
@@ -1171,21 +1188,22 @@ module ActiveRecord
1171
1188
 
1172
1189
  # For cases where a table doesn't exist like loading from schema cache
1173
1190
  def current_version
1174
- MigrationContext.new(migrations_paths).current_version
1191
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
1175
1192
  end
1176
1193
  end
1177
1194
 
1178
1195
  self.migrations_paths = ["db/migrate"]
1179
1196
 
1180
- def initialize(direction, migrations, target_version = nil)
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1181
1198
  @direction = direction
1182
1199
  @target_version = target_version
1183
1200
  @migrated_versions = nil
1184
1201
  @migrations = migrations
1202
+ @schema_migration = schema_migration
1185
1203
 
1186
1204
  validate(@migrations)
1187
1205
 
1188
- ActiveRecord::SchemaMigration.create_table
1206
+ @schema_migration.create_table
1189
1207
  ActiveRecord::InternalMetadata.create_table
1190
1208
  end
1191
1209
 
@@ -1239,7 +1257,7 @@ module ActiveRecord
1239
1257
  end
1240
1258
 
1241
1259
  def load_migrated
1242
- @migrated_versions = Set.new(Base.connection.migration_context.get_all_versions)
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1243
1261
  end
1244
1262
 
1245
1263
  private
@@ -1323,10 +1341,10 @@ module ActiveRecord
1323
1341
  def record_version_state_after_migrating(version)
1324
1342
  if down?
1325
1343
  migrated.delete(version)
1326
- ActiveRecord::SchemaMigration.where(version: version.to_s).delete_all
1344
+ @schema_migration.delete_by(version: version.to_s)
1327
1345
  else
1328
1346
  migrated << version
1329
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
1347
+ @schema_migration.create!(version: version.to_s)
1330
1348
  end
1331
1349
  end
1332
1350