activerecord 6.0.0.beta2 → 6.0.2.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 +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -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 +10 -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 +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- 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 +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- 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/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- 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 +2 -2
- 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_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- 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/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- 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 +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- 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 +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- 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 +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- 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/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -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 +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- 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 +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -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 +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -70,7 +70,15 @@ module ActiveRecord
|
|
70
70
|
predicates == other.predicates
|
71
71
|
end
|
72
72
|
|
73
|
-
def invert
|
73
|
+
def invert(as = :nand)
|
74
|
+
if predicates.size == 1
|
75
|
+
inverted_predicates = [ invert_predicate(predicates.first) ]
|
76
|
+
elsif as == :nor
|
77
|
+
inverted_predicates = predicates.map { |node| invert_predicate(node) }
|
78
|
+
else
|
79
|
+
inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
|
80
|
+
end
|
81
|
+
|
74
82
|
WhereClause.new(inverted_predicates)
|
75
83
|
end
|
76
84
|
|
@@ -115,10 +123,6 @@ module ActiveRecord
|
|
115
123
|
node.respond_to?(:operator) && node.operator == :==
|
116
124
|
end
|
117
125
|
|
118
|
-
def inverted_predicates
|
119
|
-
predicates.map { |node| invert_predicate(node) }
|
120
|
-
end
|
121
|
-
|
122
126
|
def invert_predicate(node)
|
123
127
|
case node
|
124
128
|
when NilClass
|
@@ -61,8 +61,9 @@ module ActiveRecord
|
|
61
61
|
# # => "id ASC"
|
62
62
|
def sanitize_sql_for_order(condition)
|
63
63
|
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
64
|
-
disallow_raw_sql!(
|
65
|
-
|
64
|
+
disallow_raw_sql!(
|
65
|
+
[condition.first],
|
66
|
+
permit: connection.column_name_with_order_matcher
|
66
67
|
)
|
67
68
|
|
68
69
|
# Ensure we aren't dealing with a subclass of String that might
|
@@ -133,6 +134,33 @@ module ActiveRecord
|
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
137
|
+
def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
|
138
|
+
unexpected = nil
|
139
|
+
args.each do |arg|
|
140
|
+
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
|
141
|
+
(unexpected ||= []) << arg
|
142
|
+
end
|
143
|
+
|
144
|
+
return unless unexpected
|
145
|
+
|
146
|
+
if allow_unsafe_raw_sql == :deprecated
|
147
|
+
ActiveSupport::Deprecation.warn(
|
148
|
+
"Dangerous query method (method whose arguments are used as raw " \
|
149
|
+
"SQL) called with non-attribute argument(s): " \
|
150
|
+
"#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
|
151
|
+
"arguments will be disallowed in Rails 6.1. This method should " \
|
152
|
+
"not be called with user-provided values, such as request " \
|
153
|
+
"parameters or model attributes. Known-safe values can be passed " \
|
154
|
+
"by wrapping them in Arel.sql()."
|
155
|
+
)
|
156
|
+
else
|
157
|
+
raise(ActiveRecord::UnknownAttributeReference,
|
158
|
+
"Query method called with non-attribute argument(s): " +
|
159
|
+
unexpected.map(&:inspect).join(", ")
|
160
|
+
)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
136
164
|
private
|
137
165
|
def replace_bind_variables(statement, values)
|
138
166
|
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
@@ -165,10 +193,11 @@ module ActiveRecord
|
|
165
193
|
|
166
194
|
def quote_bound_value(value, c = connection)
|
167
195
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
168
|
-
|
196
|
+
quoted = value.map { |v| c.quote(v) }
|
197
|
+
if quoted.empty?
|
169
198
|
c.quote(nil)
|
170
199
|
else
|
171
|
-
|
200
|
+
quoted.join(",")
|
172
201
|
end
|
173
202
|
else
|
174
203
|
c.quote(value)
|
data/lib/active_record/schema.rb
CHANGED
@@ -47,6 +47,7 @@ module ActiveRecord
|
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
50
|
+
attr_accessor :table_name
|
50
51
|
|
51
52
|
def initialize(connection, options = {})
|
52
53
|
@connection = connection
|
@@ -110,6 +111,8 @@ HEADER
|
|
110
111
|
def table(table, stream)
|
111
112
|
columns = @connection.columns(table)
|
112
113
|
begin
|
114
|
+
self.table_name = table
|
115
|
+
|
113
116
|
tbl = StringIO.new
|
114
117
|
|
115
118
|
# first dump primary key column
|
@@ -143,7 +146,11 @@ HEADER
|
|
143
146
|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
144
147
|
next if column.name == pk
|
145
148
|
type, colspec = column_spec(column)
|
146
|
-
|
149
|
+
if type.is_a?(Symbol)
|
150
|
+
tbl.print " t.#{type} #{column.name.inspect}"
|
151
|
+
else
|
152
|
+
tbl.print " t.column #{column.name.inspect}, #{type.inspect}"
|
153
|
+
end
|
147
154
|
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
|
148
155
|
tbl.puts
|
149
156
|
end
|
@@ -159,6 +166,8 @@ HEADER
|
|
159
166
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
160
167
|
stream.puts "# #{e.message}"
|
161
168
|
stream.puts
|
169
|
+
ensure
|
170
|
+
self.table_name = nil
|
162
171
|
end
|
163
172
|
end
|
164
173
|
|
@@ -86,8 +86,8 @@ module ActiveRecord
|
|
86
86
|
# # Should return a scope, you can call 'super' here etc.
|
87
87
|
# end
|
88
88
|
# end
|
89
|
-
def default_scope(scope = nil) # :doc:
|
90
|
-
scope =
|
89
|
+
def default_scope(scope = nil, &block) # :doc:
|
90
|
+
scope = block if block_given?
|
91
91
|
|
92
92
|
if scope.is_a?(Relation) || !scope.respond_to?(:call)
|
93
93
|
raise ArgumentError,
|
@@ -100,7 +100,7 @@ module ActiveRecord
|
|
100
100
|
self.default_scopes += [scope]
|
101
101
|
end
|
102
102
|
|
103
|
-
def build_default_scope(
|
103
|
+
def build_default_scope(relation = relation())
|
104
104
|
return if abstract_class?
|
105
105
|
|
106
106
|
if default_scope_override.nil?
|
@@ -111,15 +111,14 @@ module ActiveRecord
|
|
111
111
|
# The user has defined their own default scope method, so call that
|
112
112
|
evaluate_default_scope do
|
113
113
|
if scope = default_scope
|
114
|
-
|
114
|
+
relation.merge!(scope)
|
115
115
|
end
|
116
116
|
end
|
117
117
|
elsif default_scopes.any?
|
118
|
-
base_rel ||= relation
|
119
118
|
evaluate_default_scope do
|
120
|
-
default_scopes.inject(
|
119
|
+
default_scopes.inject(relation) do |default_scope, scope|
|
121
120
|
scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
|
122
|
-
default_scope.
|
121
|
+
default_scope.instance_exec(&scope) || default_scope
|
123
122
|
end
|
124
123
|
end
|
125
124
|
end
|
@@ -31,7 +31,8 @@ module ActiveRecord
|
|
31
31
|
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
32
32
|
Class level methods will no longer inherit scoping from `#{scope._deprecated_scope_source}`
|
33
33
|
in Rails 6.1. To continue using the scoped relation, pass it into the block directly.
|
34
|
-
To instead access the full set of models, as Rails 6.1 will, use `#{name}.unscoped
|
34
|
+
To instead access the full set of models, as Rails 6.1 will, use `#{name}.unscoped`,
|
35
|
+
or `#{name}.default_scoped` if a model has default scopes.
|
35
36
|
MSG
|
36
37
|
end
|
37
38
|
|
@@ -58,7 +59,7 @@ module ActiveRecord
|
|
58
59
|
end
|
59
60
|
|
60
61
|
def default_extensions # :nodoc:
|
61
|
-
if scope =
|
62
|
+
if scope = scope_for_association || build_default_scope
|
62
63
|
scope.extensions
|
63
64
|
else
|
64
65
|
[]
|
@@ -113,8 +113,8 @@ module ActiveRecord
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
def self.create(connection,
|
117
|
-
relation = block.call Params.new
|
116
|
+
def self.create(connection, callable = nil, &block)
|
117
|
+
relation = (callable || block).call Params.new
|
118
118
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
119
119
|
bind_map = BindMap.new(binds)
|
120
120
|
new(query_builder, bind_map, relation.klass)
|
data/lib/active_record/store.rb
CHANGED
@@ -11,6 +11,12 @@ module ActiveRecord
|
|
11
11
|
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
|
12
12
|
# already built around just accessing attributes on the model.
|
13
13
|
#
|
14
|
+
# Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
|
15
|
+
# methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
|
16
|
+
# +key_before_last_save+).
|
17
|
+
#
|
18
|
+
# NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
|
19
|
+
#
|
14
20
|
# Make sure that you declare the database column used for the serialized store as a text, so there's
|
15
21
|
# plenty of room.
|
16
22
|
#
|
@@ -49,6 +55,12 @@ module ActiveRecord
|
|
49
55
|
# u.settings[:country] # => 'Denmark'
|
50
56
|
# u.settings['country'] # => 'Denmark'
|
51
57
|
#
|
58
|
+
# # Dirty tracking
|
59
|
+
# u.color = 'green'
|
60
|
+
# u.color_changed? # => true
|
61
|
+
# u.color_was # => 'black'
|
62
|
+
# u.color_change # => ['black', 'red']
|
63
|
+
#
|
52
64
|
# # Add additional accessors to an existing store through store_accessor
|
53
65
|
# class SuperUser < User
|
54
66
|
# store_accessor :settings, :privileges, :servants
|
@@ -127,6 +139,42 @@ module ActiveRecord
|
|
127
139
|
define_method(accessor_key) do
|
128
140
|
read_store_attribute(store_attribute, key)
|
129
141
|
end
|
142
|
+
|
143
|
+
define_method("#{accessor_key}_changed?") do
|
144
|
+
return false unless attribute_changed?(store_attribute)
|
145
|
+
prev_store, new_store = changes[store_attribute]
|
146
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
define_method("#{accessor_key}_change") do
|
150
|
+
return unless attribute_changed?(store_attribute)
|
151
|
+
prev_store, new_store = changes[store_attribute]
|
152
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
153
|
+
end
|
154
|
+
|
155
|
+
define_method("#{accessor_key}_was") do
|
156
|
+
return unless attribute_changed?(store_attribute)
|
157
|
+
prev_store, _new_store = changes[store_attribute]
|
158
|
+
prev_store&.dig(key)
|
159
|
+
end
|
160
|
+
|
161
|
+
define_method("saved_change_to_#{accessor_key}?") do
|
162
|
+
return false unless saved_change_to_attribute?(store_attribute)
|
163
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
164
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
165
|
+
end
|
166
|
+
|
167
|
+
define_method("saved_change_to_#{accessor_key}") do
|
168
|
+
return unless saved_change_to_attribute?(store_attribute)
|
169
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
170
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
171
|
+
end
|
172
|
+
|
173
|
+
define_method("#{accessor_key}_before_last_save") do
|
174
|
+
return unless saved_change_to_attribute?(store_attribute)
|
175
|
+
prev_store, _new_store = saved_change_to_attribute(store_attribute)
|
176
|
+
prev_store&.dig(key)
|
177
|
+
end
|
130
178
|
end
|
131
179
|
end
|
132
180
|
|
@@ -4,17 +4,18 @@ module ActiveRecord
|
|
4
4
|
class TableMetadata # :nodoc:
|
5
5
|
delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
|
6
6
|
|
7
|
-
def initialize(klass, arel_table, association = nil)
|
7
|
+
def initialize(klass, arel_table, association = nil, types = klass)
|
8
8
|
@klass = klass
|
9
|
+
@types = types
|
9
10
|
@arel_table = arel_table
|
10
11
|
@association = association
|
11
12
|
end
|
12
13
|
|
13
14
|
def resolve_column_aliases(hash)
|
14
15
|
new_hash = hash.dup
|
15
|
-
hash.
|
16
|
-
if
|
17
|
-
new_hash[
|
16
|
+
hash.each_key do |key|
|
17
|
+
if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
|
18
|
+
new_hash[new_key] = new_hash.delete(key)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
new_hash
|
@@ -29,11 +30,7 @@ module ActiveRecord
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def type(column_name)
|
32
|
-
|
33
|
-
klass.type_for_attribute(column_name)
|
34
|
-
else
|
35
|
-
Type.default_value
|
36
|
-
end
|
33
|
+
types.type_for_attribute(column_name)
|
37
34
|
end
|
38
35
|
|
39
36
|
def has_column?(column_name)
|
@@ -52,13 +49,12 @@ module ActiveRecord
|
|
52
49
|
elsif association && !association.polymorphic?
|
53
50
|
association_klass = association.klass
|
54
51
|
arel_table = association_klass.arel_table.alias(table_name)
|
52
|
+
TableMetadata.new(association_klass, arel_table, association)
|
55
53
|
else
|
56
54
|
type_caster = TypeCaster::Connection.new(klass, table_name)
|
57
|
-
association_klass = nil
|
58
55
|
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
|
56
|
+
TableMetadata.new(nil, arel_table, association, type_caster)
|
59
57
|
end
|
60
|
-
|
61
|
-
TableMetadata.new(association_klass, arel_table, association)
|
62
58
|
end
|
63
59
|
|
64
60
|
def polymorphic_association?
|
@@ -74,6 +70,6 @@ module ActiveRecord
|
|
74
70
|
end
|
75
71
|
|
76
72
|
private
|
77
|
-
attr_reader :klass, :arel_table, :association
|
73
|
+
attr_reader :klass, :types, :arel_table, :association
|
78
74
|
end
|
79
75
|
end
|
@@ -141,8 +141,21 @@ module ActiveRecord
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
def
|
145
|
-
|
144
|
+
def setup_initial_database_yaml
|
145
|
+
return {} unless defined?(Rails)
|
146
|
+
|
147
|
+
begin
|
148
|
+
Rails.application.config.load_database_yaml
|
149
|
+
rescue
|
150
|
+
$stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
|
151
|
+
|
152
|
+
{}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def for_each(databases)
|
157
|
+
return {} unless defined?(Rails)
|
158
|
+
|
146
159
|
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
|
147
160
|
|
148
161
|
# if this is a single database application we don't want tasks for each primary database
|
@@ -153,8 +166,22 @@ module ActiveRecord
|
|
153
166
|
end
|
154
167
|
end
|
155
168
|
|
156
|
-
def
|
157
|
-
|
169
|
+
def raise_for_multi_db(environment = env, command:)
|
170
|
+
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
|
171
|
+
|
172
|
+
if db_configs.count > 1
|
173
|
+
dbs_list = []
|
174
|
+
|
175
|
+
db_configs.each do |db|
|
176
|
+
dbs_list << "#{command}:#{db.spec_name}"
|
177
|
+
end
|
178
|
+
|
179
|
+
raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def create_current(environment = env, spec_name = nil)
|
184
|
+
each_current_configuration(environment, spec_name) { |configuration|
|
158
185
|
create configuration
|
159
186
|
}
|
160
187
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
@@ -182,6 +209,26 @@ module ActiveRecord
|
|
182
209
|
}
|
183
210
|
end
|
184
211
|
|
212
|
+
def truncate_tables(configuration)
|
213
|
+
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
|
214
|
+
conn = ActiveRecord::Base.connection
|
215
|
+
table_names = conn.tables
|
216
|
+
table_names -= [
|
217
|
+
conn.schema_migration.table_name,
|
218
|
+
InternalMetadata.table_name
|
219
|
+
]
|
220
|
+
|
221
|
+
ActiveRecord::Base.connection.truncate_tables(*table_names)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
private :truncate_tables
|
225
|
+
|
226
|
+
def truncate_all(environment = env)
|
227
|
+
ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
|
228
|
+
truncate_tables db_config.config
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
185
232
|
def migrate
|
186
233
|
check_target_version
|
187
234
|
|
@@ -198,7 +245,7 @@ module ActiveRecord
|
|
198
245
|
end
|
199
246
|
|
200
247
|
def migrate_status
|
201
|
-
unless ActiveRecord::
|
248
|
+
unless ActiveRecord::Base.connection.schema_migration.table_exists?
|
202
249
|
Kernel.abort "Schema migrations table does not exist yet."
|
203
250
|
end
|
204
251
|
|
@@ -286,10 +333,60 @@ module ActiveRecord
|
|
286
333
|
end
|
287
334
|
ActiveRecord::InternalMetadata.create_table
|
288
335
|
ActiveRecord::InternalMetadata[:environment] = environment
|
336
|
+
ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
|
289
337
|
ensure
|
290
338
|
Migration.verbose = verbose_was
|
291
339
|
end
|
292
340
|
|
341
|
+
def schema_up_to_date?(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary")
|
342
|
+
file ||= dump_filename(spec_name, format)
|
343
|
+
|
344
|
+
return true unless File.exist?(file)
|
345
|
+
|
346
|
+
ActiveRecord::Base.establish_connection(configuration)
|
347
|
+
return false unless ActiveRecord::InternalMetadata.table_exists?
|
348
|
+
ActiveRecord::InternalMetadata[:schema_sha1] == schema_sha1(file)
|
349
|
+
end
|
350
|
+
|
351
|
+
def reconstruct_from_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
|
352
|
+
file ||= dump_filename(spec_name, format)
|
353
|
+
|
354
|
+
check_schema_file(file)
|
355
|
+
|
356
|
+
ActiveRecord::Base.establish_connection(configuration)
|
357
|
+
|
358
|
+
if schema_up_to_date?(configuration, format, file, environment, spec_name)
|
359
|
+
truncate_tables(configuration)
|
360
|
+
else
|
361
|
+
purge(configuration)
|
362
|
+
load_schema(configuration, format, file, environment, spec_name)
|
363
|
+
end
|
364
|
+
rescue ActiveRecord::NoDatabaseError
|
365
|
+
create(configuration)
|
366
|
+
load_schema(configuration, format, file, environment, spec_name)
|
367
|
+
end
|
368
|
+
|
369
|
+
def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
|
370
|
+
require "active_record/schema_dumper"
|
371
|
+
filename = dump_filename(spec_name, format)
|
372
|
+
connection = ActiveRecord::Base.connection
|
373
|
+
|
374
|
+
case format
|
375
|
+
when :ruby
|
376
|
+
File.open(filename, "w:utf-8") do |file|
|
377
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
378
|
+
end
|
379
|
+
when :sql
|
380
|
+
structure_dump(configuration, filename)
|
381
|
+
if connection.schema_migration.table_exists?
|
382
|
+
File.open(filename, "a") do |f|
|
383
|
+
f.puts connection.dump_schema_information
|
384
|
+
f.print "\n"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
293
390
|
def schema_file(format = ActiveRecord::Base.schema_format)
|
294
391
|
File.join(db_dir, schema_file_type(format))
|
295
392
|
end
|
@@ -371,12 +468,14 @@ module ActiveRecord
|
|
371
468
|
task.is_a?(String) ? task.constantize : task
|
372
469
|
end
|
373
470
|
|
374
|
-
def each_current_configuration(environment)
|
471
|
+
def each_current_configuration(environment, spec_name = nil)
|
375
472
|
environments = [environment]
|
376
473
|
environments << "test" if environment == "development"
|
377
474
|
|
378
475
|
environments.each do |env|
|
379
476
|
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
477
|
+
next if spec_name && spec_name != db_config.spec_name
|
478
|
+
|
380
479
|
yield db_config.config, db_config.spec_name, env
|
381
480
|
end
|
382
481
|
end
|
@@ -398,6 +497,10 @@ module ActiveRecord
|
|
398
497
|
def local_database?(configuration)
|
399
498
|
configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
|
400
499
|
end
|
500
|
+
|
501
|
+
def schema_sha1(file)
|
502
|
+
Digest::SHA1.hexdigest(File.read(file))
|
503
|
+
end
|
401
504
|
end
|
402
505
|
end
|
403
506
|
end
|