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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- 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
|
-
#
|
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}#{
|
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
|
-
|
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
|
-
|
113
|
+
@primary_key => id_in_database,
|
115
114
|
locking_column => read_attribute_before_type_cast(locking_column)
|
116
115
|
)
|
117
116
|
|
@@ -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,
|
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
|
491
|
-
#
|
492
|
-
#
|
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
|
530
|
+
class Current < Migration #:nodoc:
|
524
531
|
end
|
525
532
|
|
526
|
-
def self.inherited(subclass)
|
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/
|
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
|
572
|
-
attr_accessor :disable_ddl_transaction
|
578
|
+
attr_accessor :delegate #:nodoc:
|
579
|
+
attr_accessor :disable_ddl_transaction #:nodoc:
|
573
580
|
|
574
|
-
def nearest_delegate
|
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
|
-
|
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!
|
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)
|
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
|
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
|
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
|
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,
|
1079
|
+
Migrator.new(:up, migrations, schema_migration)
|
1063
1080
|
end
|
1064
1081
|
|
1065
1082
|
def get_all_versions
|
1066
|
-
if
|
1067
|
-
|
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 =
|
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 =
|
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
|
-
|
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(
|
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
|
-
|
1344
|
+
@schema_migration.delete_by(version: version.to_s)
|
1327
1345
|
else
|
1328
1346
|
migrated << version
|
1329
|
-
|
1347
|
+
@schema_migration.create!(version: version.to_s)
|
1330
1348
|
end
|
1331
1349
|
end
|
1332
1350
|
|