activerecord 6.0.0.rc2 → 6.0.0
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 +27 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +11 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -7
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/database_configurations.rb +60 -31
- data/lib/active_record/enum.rb +9 -0
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +1 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +7 -5
- data/lib/active_record/migration.rb +13 -5
- data/lib/active_record/railties/databases.rake +6 -3
- data/lib/active_record/relation/calculations.rb +1 -3
- data/lib/active_record/relation/query_methods.rb +32 -18
- data/lib/active_record/tasks/database_tasks.rb +35 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/validations.rb +1 -0
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed17e948626d108075b2951ac71abbac3075ef55b055abe64e2b352e91755fd9
|
4
|
+
data.tar.gz: 2c031642e021041f9fdf278888b580fd446879008a23f12d33c8b33aafbddc59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b208a5cbee77c2a057649b762a92ec4ca5be3b8fbd1b2123caf485579abbc602bcb2aee455a503b74f33efa9ce364ab644dc8958b9e1edd94fee3397bf17cc8
|
7
|
+
data.tar.gz: 751ecf72279d478887665f6a3efc5ad366b70284fb3f4ef6d2f3b072faf7c325be660116a7d3ac9427ac44dcf6f8c345e145921692d0465e24a70f1c2ca3b1bd
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,28 @@
|
|
1
|
+
## Rails 6.0.0 (August 16, 2019) ##
|
2
|
+
|
3
|
+
* Preserve user supplied joins order as much as possible.
|
4
|
+
|
5
|
+
Fixes #36761, #34328, #24281, #12953.
|
6
|
+
|
7
|
+
*Ryuta Kamizono*
|
8
|
+
|
9
|
+
* Make the DATABASE_URL env variable only affect the primary connection. Add new env variables for multiple databases.
|
10
|
+
|
11
|
+
*John Crepezzi*, *Eileen Uchitelle*
|
12
|
+
|
13
|
+
* Add a warning for enum elements with 'not_' prefix.
|
14
|
+
|
15
|
+
class Foo
|
16
|
+
enum status: [:sent, :not_sent]
|
17
|
+
end
|
18
|
+
|
19
|
+
*Edu Depetris*
|
20
|
+
|
21
|
+
* Make currency symbols optional for money column type in PostgreSQL
|
22
|
+
|
23
|
+
*Joel Schneider*
|
24
|
+
|
25
|
+
|
1
26
|
## Rails 6.0.0.rc2 (July 22, 2019) ##
|
2
27
|
|
3
28
|
* Add database_exists? method to connection adapters to check if a database exists.
|
@@ -386,7 +411,7 @@
|
|
386
411
|
`GET` and `HEAD` requests will read from the replica unless there was
|
387
412
|
a write in the last 2 seconds, otherwise they will read from the primary.
|
388
413
|
Non-get requests will always write to the primary. The middleware accepts
|
389
|
-
an argument for a Resolver class and
|
414
|
+
an argument for a Resolver class and an Operations class where you are able
|
390
415
|
to change how the auto-switcher works to be most beneficial for your
|
391
416
|
application.
|
392
417
|
|
@@ -820,7 +845,7 @@
|
|
820
845
|
*Darwin Wu*
|
821
846
|
|
822
847
|
* Configuration item `config.filter_parameters` could also filter out
|
823
|
-
sensitive values of database columns when
|
848
|
+
sensitive values of database columns when calling `#inspect`.
|
824
849
|
We also added `ActiveRecord::Base::filter_attributes`/`=` in order to
|
825
850
|
specify sensitive attributes to specific model.
|
826
851
|
|
@@ -63,7 +63,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
63
63
|
|
64
64
|
def middle_reflection(join_model)
|
65
65
|
middle_name = [lhs_model.name.downcase.pluralize,
|
66
|
-
association_name].join("_").gsub("::", "_").to_sym
|
66
|
+
association_name.to_s].sort.join("_").gsub("::", "_").to_sym
|
67
67
|
middle_options = middle_options join_model
|
68
68
|
|
69
69
|
HasMany.create_reflection(lhs_model,
|
@@ -304,7 +304,7 @@ module ActiveRecord
|
|
304
304
|
def validate_single_association(reflection)
|
305
305
|
association = association_instance_get(reflection.name)
|
306
306
|
record = association && association.reader
|
307
|
-
association_valid?(reflection, record) if record
|
307
|
+
association_valid?(reflection, record) if record && record.changed_for_autosave?
|
308
308
|
end
|
309
309
|
|
310
310
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -1005,28 +1005,33 @@ module ActiveRecord
|
|
1005
1005
|
end
|
1006
1006
|
end
|
1007
1007
|
|
1008
|
-
attr_reader :prevent_writes
|
1009
|
-
|
1010
1008
|
def initialize
|
1011
1009
|
# These caches are keyed by spec.name (ConnectionSpecification#name).
|
1012
1010
|
@owner_to_pool = ConnectionHandler.create_owner_to_pool
|
1013
|
-
@prevent_writes = false
|
1014
1011
|
|
1015
1012
|
# Backup finalizer: if the forked child never needed a pool, the above
|
1016
1013
|
# early discard has not occurred
|
1017
1014
|
ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
|
1018
1015
|
end
|
1019
1016
|
|
1017
|
+
def prevent_writes # :nodoc:
|
1018
|
+
Thread.current[:prevent_writes]
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def prevent_writes=(prevent_writes) # :nodoc:
|
1022
|
+
Thread.current[:prevent_writes] = prevent_writes
|
1023
|
+
end
|
1024
|
+
|
1020
1025
|
# Prevent writing to the database regardless of role.
|
1021
1026
|
#
|
1022
1027
|
# In some cases you may want to prevent writes to the database
|
1023
1028
|
# even if you are on a database that can write. `while_preventing_writes`
|
1024
1029
|
# will prevent writes to the database for the duration of the block.
|
1025
|
-
def while_preventing_writes
|
1026
|
-
original,
|
1030
|
+
def while_preventing_writes(enabled = true)
|
1031
|
+
original, self.prevent_writes = self.prevent_writes, enabled
|
1027
1032
|
yield
|
1028
1033
|
ensure
|
1029
|
-
|
1034
|
+
self.prevent_writes = original
|
1030
1035
|
end
|
1031
1036
|
|
1032
1037
|
def connection_pool_list
|
@@ -101,7 +101,7 @@ module ActiveRecord
|
|
101
101
|
def index_exists?(table_name, column_name, options = {})
|
102
102
|
column_names = Array(column_name).map(&:to_s)
|
103
103
|
checks = []
|
104
|
-
checks << lambda { |i| i.columns == column_names }
|
104
|
+
checks << lambda { |i| Array(i.columns) == column_names }
|
105
105
|
checks << lambda { |i| i.unique } if options[:unique]
|
106
106
|
checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
|
107
107
|
|
@@ -11,6 +11,7 @@ require "arel/collectors/bind"
|
|
11
11
|
require "arel/collectors/composite"
|
12
12
|
require "arel/collectors/sql_string"
|
13
13
|
require "arel/collectors/substitute_binds"
|
14
|
+
require "concurrent/atomic/thread_local_var"
|
14
15
|
|
15
16
|
module ActiveRecord
|
16
17
|
module ConnectionAdapters # :nodoc:
|
@@ -78,7 +79,7 @@ module ActiveRecord
|
|
78
79
|
SIMPLE_INT = /\A\d+\z/
|
79
80
|
|
80
81
|
attr_accessor :pool
|
81
|
-
attr_reader :visitor, :owner, :logger, :lock
|
82
|
+
attr_reader :visitor, :owner, :logger, :lock
|
82
83
|
alias :in_use? :owner
|
83
84
|
|
84
85
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
@@ -129,10 +130,10 @@ module ActiveRecord
|
|
129
130
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
130
131
|
|
131
132
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
132
|
-
@
|
133
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
|
133
134
|
@visitor.extend(DetermineIfPreparableVisitor)
|
134
135
|
else
|
135
|
-
@
|
136
|
+
@prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
|
136
137
|
end
|
137
138
|
|
138
139
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
@@ -175,6 +176,10 @@ module ActiveRecord
|
|
175
176
|
end
|
176
177
|
end
|
177
178
|
|
179
|
+
def prepared_statements
|
180
|
+
@prepared_statement_status.value
|
181
|
+
end
|
182
|
+
|
178
183
|
class Version
|
179
184
|
include Comparable
|
180
185
|
|
@@ -259,10 +264,7 @@ module ActiveRecord
|
|
259
264
|
end
|
260
265
|
|
261
266
|
def unprepared_statement
|
262
|
-
|
263
|
-
yield
|
264
|
-
ensure
|
265
|
-
@prepared_statements = old_prepared_statements
|
267
|
+
@prepared_statement_status.bind(false) { yield }
|
266
268
|
end
|
267
269
|
|
268
270
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -222,7 +222,7 @@ module ActiveRecord
|
|
222
222
|
when Hash
|
223
223
|
resolve_hash_connection config_or_env
|
224
224
|
else
|
225
|
-
|
225
|
+
raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config_or_env.inspect}"
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
@@ -39,8 +39,8 @@ module ActiveRecord
|
|
39
39
|
include MySQL::DatabaseStatements
|
40
40
|
|
41
41
|
def initialize(connection, logger, connection_options, config)
|
42
|
-
|
43
|
-
|
42
|
+
superclass_config = config.reverse_merge(prepared_statements: false)
|
43
|
+
super(connection, logger, connection_options, superclass_config)
|
44
44
|
configure_connection
|
45
45
|
end
|
46
46
|
|
@@ -26,9 +26,9 @@ module ActiveRecord
|
|
26
26
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
28
28
|
case value
|
29
|
-
when /^-?\D
|
29
|
+
when /^-?\D*[\d,]+\.\d{2}$/ # (1)
|
30
30
|
value.gsub!(/[^-\d.]/, "")
|
31
|
-
when /^-?\D
|
31
|
+
when /^-?\D*[\d.]+,\d{2}$/ # (2)
|
32
32
|
value.gsub!(/[^-\d,]/, "").sub!(/,/, ".")
|
33
33
|
end
|
34
34
|
|
@@ -9,6 +9,8 @@ module ActiveRecord
|
|
9
9
|
# objects (either a HashConfig or UrlConfig) that are constructed from the
|
10
10
|
# application's database configuration hash or URL string.
|
11
11
|
class DatabaseConfigurations
|
12
|
+
class InvalidConfigurationError < StandardError; end
|
13
|
+
|
12
14
|
attr_reader :configurations
|
13
15
|
delegate :any?, to: :configurations
|
14
16
|
|
@@ -91,6 +93,19 @@ module ActiveRecord
|
|
91
93
|
end
|
92
94
|
alias :blank? :empty?
|
93
95
|
|
96
|
+
def each
|
97
|
+
throw_getter_deprecation(:each)
|
98
|
+
configurations.each { |config|
|
99
|
+
yield [config.env_name, config.config]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def first
|
104
|
+
throw_getter_deprecation(:first)
|
105
|
+
config = configurations.first
|
106
|
+
[config.env_name, config.config]
|
107
|
+
end
|
108
|
+
|
94
109
|
private
|
95
110
|
def env_with_configs(env = nil)
|
96
111
|
if env
|
@@ -104,34 +119,48 @@ module ActiveRecord
|
|
104
119
|
return configs.configurations if configs.is_a?(DatabaseConfigurations)
|
105
120
|
return configs if configs.is_a?(Array)
|
106
121
|
|
107
|
-
|
108
|
-
|
109
|
-
|
122
|
+
db_configs = configs.flat_map do |env_name, config|
|
123
|
+
if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
|
124
|
+
walk_configs(env_name.to_s, config)
|
125
|
+
else
|
126
|
+
build_db_config_from_raw_config(env_name.to_s, "primary", config)
|
127
|
+
end
|
128
|
+
end
|
110
129
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
130
|
+
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
|
131
|
+
|
132
|
+
unless db_configs.find(&:for_current_env?)
|
133
|
+
db_configs << environment_url_config(current_env, "primary", {})
|
134
|
+
end
|
135
|
+
|
136
|
+
merge_db_environment_variables(current_env, db_configs.compact)
|
137
|
+
end
|
138
|
+
|
139
|
+
def walk_configs(env_name, config)
|
140
|
+
config.map do |spec_name, sub_config|
|
141
|
+
build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
|
115
142
|
end
|
116
143
|
end
|
117
144
|
|
118
|
-
def
|
145
|
+
def build_db_config_from_raw_config(env_name, spec_name, config)
|
119
146
|
case config
|
120
147
|
when String
|
121
148
|
build_db_config_from_string(env_name, spec_name, config)
|
122
149
|
when Hash
|
123
150
|
build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
|
151
|
+
else
|
152
|
+
raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
|
124
153
|
end
|
125
154
|
end
|
126
155
|
|
127
156
|
def build_db_config_from_string(env_name, spec_name, config)
|
128
157
|
url = config
|
129
158
|
uri = URI.parse(url)
|
130
|
-
if uri.
|
159
|
+
if uri.scheme
|
131
160
|
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
|
161
|
+
else
|
162
|
+
raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
|
132
163
|
end
|
133
|
-
rescue URI::InvalidURIError
|
134
|
-
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
135
164
|
end
|
136
165
|
|
137
166
|
def build_db_config_from_hash(env_name, spec_name, config)
|
@@ -141,36 +170,36 @@ module ActiveRecord
|
|
141
170
|
config_without_url.delete "url"
|
142
171
|
|
143
172
|
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
|
144
|
-
elsif config["database"] || config["adapter"] || ENV["DATABASE_URL"]
|
145
|
-
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
146
173
|
else
|
147
|
-
|
148
|
-
walk_configs(env_name, sub_spec_name, sub_config)
|
149
|
-
end
|
174
|
+
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
150
175
|
end
|
151
176
|
end
|
152
177
|
|
153
|
-
def
|
154
|
-
|
178
|
+
def merge_db_environment_variables(current_env, configs)
|
179
|
+
configs.map do |config|
|
180
|
+
next config if config.url_config? || config.env_name != current_env
|
155
181
|
|
156
|
-
|
157
|
-
|
158
|
-
if config.url_config?
|
159
|
-
config
|
160
|
-
else
|
161
|
-
ActiveRecord::DatabaseConfigurations::UrlConfig.new(config.env_name, config.spec_name, url, config.config)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
else
|
165
|
-
configs + [ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, "primary", url)]
|
182
|
+
url_config = environment_url_config(current_env, config.spec_name, config.config)
|
183
|
+
url_config || config
|
166
184
|
end
|
167
185
|
end
|
168
186
|
|
187
|
+
def environment_url_config(env, spec_name, config)
|
188
|
+
url = environment_value_for(spec_name)
|
189
|
+
return unless url
|
190
|
+
|
191
|
+
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
|
192
|
+
end
|
193
|
+
|
194
|
+
def environment_value_for(spec_name)
|
195
|
+
spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
|
196
|
+
url = ENV[spec_env_key]
|
197
|
+
url ||= ENV["DATABASE_URL"] if spec_name == "primary"
|
198
|
+
url
|
199
|
+
end
|
200
|
+
|
169
201
|
def method_missing(method, *args, &blk)
|
170
202
|
case method
|
171
|
-
when :each, :first
|
172
|
-
throw_getter_deprecation(method)
|
173
|
-
configurations.send(method, *args, &blk)
|
174
203
|
when :fetch
|
175
204
|
throw_getter_deprecation(method)
|
176
205
|
configs_for(env_name: args.first)
|
data/lib/active_record/enum.rb
CHANGED
@@ -200,6 +200,8 @@ module ActiveRecord
|
|
200
200
|
# scope :active, -> { where(status: 0) }
|
201
201
|
# scope :not_active, -> { where.not(status: 0) }
|
202
202
|
if enum_scopes != false
|
203
|
+
klass.send(:detect_negative_condition!, value_method_name)
|
204
|
+
|
203
205
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
204
206
|
klass.scope value_method_name, -> { where(attr => value) }
|
205
207
|
|
@@ -261,5 +263,12 @@ module ActiveRecord
|
|
261
263
|
source: source
|
262
264
|
}
|
263
265
|
end
|
266
|
+
|
267
|
+
def detect_negative_condition!(method_name)
|
268
|
+
if method_name.start_with?("not_") && logger
|
269
|
+
logger.warn "An enum element in #{self.name} uses the prefix 'not_'." \
|
270
|
+
" This will cause a conflict with auto generated negative scopes."
|
271
|
+
end
|
272
|
+
end
|
264
273
|
end
|
265
274
|
end
|
@@ -531,15 +531,15 @@ module ActiveRecord
|
|
531
531
|
end
|
532
532
|
end
|
533
533
|
|
534
|
-
def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
|
534
|
+
def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
|
535
535
|
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
536
536
|
class_names = ClassCache.new class_names, config
|
537
537
|
|
538
538
|
# FIXME: Apparently JK uses this.
|
539
|
-
connection = block_given? ?
|
539
|
+
connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
|
540
540
|
|
541
541
|
fixture_files_to_read = fixture_set_names.reject do |fs_name|
|
542
|
-
fixture_is_cached?(connection, fs_name)
|
542
|
+
fixture_is_cached?(connection.call, fs_name)
|
543
543
|
end
|
544
544
|
|
545
545
|
if fixture_files_to_read.any?
|
@@ -549,9 +549,9 @@ module ActiveRecord
|
|
549
549
|
class_names,
|
550
550
|
connection,
|
551
551
|
)
|
552
|
-
cache_fixtures(connection, fixtures_map)
|
552
|
+
cache_fixtures(connection.call, fixtures_map)
|
553
553
|
end
|
554
|
-
cached_fixtures(connection, fixture_set_names)
|
554
|
+
cached_fixtures(connection.call, fixture_set_names)
|
555
555
|
end
|
556
556
|
|
557
557
|
# Returns a consistent, platform-independent identifier for +label+.
|
@@ -591,7 +591,11 @@ module ActiveRecord
|
|
591
591
|
|
592
592
|
def insert(fixture_sets, connection) # :nodoc:
|
593
593
|
fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
|
594
|
-
fixture_set.model_class
|
594
|
+
if fixture_set.model_class
|
595
|
+
fixture_set.model_class.connection
|
596
|
+
else
|
597
|
+
connection.call
|
598
|
+
end
|
595
599
|
end
|
596
600
|
|
597
601
|
fixture_sets_by_connection.each do |conn, set|
|
@@ -602,6 +606,7 @@ module ActiveRecord
|
|
602
606
|
table_rows_for_connection[table].unshift(*rows)
|
603
607
|
end
|
604
608
|
end
|
609
|
+
|
605
610
|
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
606
611
|
|
607
612
|
# Cap primary key sequences to max(pk).
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
47
47
|
|
48
48
|
def read_from_primary(&blk)
|
49
49
|
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
50
|
-
ActiveRecord::Base.connection_handler.while_preventing_writes do
|
50
|
+
ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
|
51
51
|
instrumenter.instrument("database_selector.active_record.read_from_primary") do
|
52
52
|
yield
|
53
53
|
end
|
@@ -65,10 +65,12 @@ module ActiveRecord
|
|
65
65
|
|
66
66
|
def write_to_primary(&blk)
|
67
67
|
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
|
69
|
+
instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
context.update_last_write_timestamp
|
73
|
+
end
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
@@ -588,18 +588,26 @@ module ActiveRecord
|
|
588
588
|
end
|
589
589
|
|
590
590
|
def load_schema_if_pending!
|
591
|
-
|
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
|
592
599
|
# Roundtrip to Rake to allow plugins to hook into database initialization.
|
593
600
|
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
|
594
601
|
FileUtils.cd(root) do
|
595
|
-
current_config = Base.connection_config
|
596
602
|
Base.clear_all_connections!
|
597
603
|
system("bin/rails db:test:prepare")
|
598
|
-
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
|
599
|
-
Base.establish_connection(current_config)
|
600
604
|
end
|
601
|
-
check_pending!
|
602
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!
|
603
611
|
end
|
604
612
|
|
605
613
|
def maintain_test_schema! #:nodoc:
|
@@ -265,6 +265,8 @@ db_namespace = namespace :db do
|
|
265
265
|
end
|
266
266
|
abort %{Run `rails db:migrate` to update your database then try again.}
|
267
267
|
end
|
268
|
+
ensure
|
269
|
+
ActiveRecord::Base.establish_connection(ActiveRecord::Tasks::DatabaseTasks.env.to_sym)
|
268
270
|
end
|
269
271
|
|
270
272
|
namespace :abort_if_pending_migrations do
|
@@ -297,10 +299,11 @@ db_namespace = namespace :db do
|
|
297
299
|
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
|
298
300
|
ActiveRecord::Base.establish_connection(db_config.config)
|
299
301
|
|
300
|
-
ActiveRecord::Tasks::DatabaseTasks.migrate
|
301
|
-
|
302
302
|
# Skipped when no database
|
303
|
-
ActiveRecord::Tasks::DatabaseTasks.
|
303
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
304
|
+
if ActiveRecord::Base.dump_schema_after_migration
|
305
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config.config, ActiveRecord::Base.schema_format, db_config.spec_name)
|
306
|
+
end
|
304
307
|
|
305
308
|
rescue ActiveRecord::NoDatabaseError
|
306
309
|
ActiveRecord::Tasks::DatabaseTasks.create_current(db_config.env_name, db_config.spec_name)
|
@@ -321,7 +321,7 @@ module ActiveRecord
|
|
321
321
|
}
|
322
322
|
group_columns = group_aliases.zip(group_fields)
|
323
323
|
|
324
|
-
aggregate_alias = column_alias_for("#{operation}
|
324
|
+
aggregate_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
325
325
|
|
326
326
|
select_values = [
|
327
327
|
operation_over_aggregate_column(
|
@@ -374,8 +374,6 @@ module ActiveRecord
|
|
374
374
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
375
375
|
# column_alias_for("count(*)") # => "count_all"
|
376
376
|
def column_alias_for(field)
|
377
|
-
return field if field.match?(/\A\w{,#{connection.table_alias_length}}\z/)
|
378
|
-
|
379
377
|
column_alias = +field
|
380
378
|
column_alias.gsub!(/\*/, "all")
|
381
379
|
column_alias.gsub!(/\W+/, " ")
|
@@ -372,7 +372,7 @@ module ActiveRecord
|
|
372
372
|
|
373
373
|
# Same as #reorder but operates on relation in-place instead of copying.
|
374
374
|
def reorder!(*args) # :nodoc:
|
375
|
-
preprocess_order_args(args)
|
375
|
+
preprocess_order_args(args) unless args.all?(&:blank?)
|
376
376
|
|
377
377
|
self.reordering_value = true
|
378
378
|
self.order_values = args
|
@@ -1095,26 +1095,44 @@ module ActiveRecord
|
|
1095
1095
|
end
|
1096
1096
|
|
1097
1097
|
def build_left_outer_joins(manager, outer_joins, aliases)
|
1098
|
-
buckets = {
|
1098
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1099
|
+
buckets[:association_join] = valid_association_list(outer_joins)
|
1099
1100
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1100
1101
|
end
|
1101
1102
|
|
1102
1103
|
def build_joins(manager, joins, aliases)
|
1104
|
+
buckets = Hash.new { |h, k| h[k] = [] }
|
1105
|
+
|
1103
1106
|
unless left_outer_joins_values.empty?
|
1104
1107
|
left_joins = valid_association_list(left_outer_joins_values.flatten)
|
1105
|
-
|
1108
|
+
buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
joins.map! do |join|
|
1112
|
+
if join.is_a?(String)
|
1113
|
+
table.create_string_join(Arel.sql(join.strip)) unless join.blank?
|
1114
|
+
else
|
1115
|
+
join
|
1116
|
+
end
|
1117
|
+
end.delete_if(&:blank?).uniq!
|
1118
|
+
|
1119
|
+
while joins.first.is_a?(Arel::Nodes::Join)
|
1120
|
+
join_node = joins.shift
|
1121
|
+
if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
|
1122
|
+
buckets[:join_node] << join_node
|
1123
|
+
else
|
1124
|
+
buckets[:leading_join] << join_node
|
1125
|
+
end
|
1106
1126
|
end
|
1107
1127
|
|
1108
|
-
|
1128
|
+
joins.each do |join|
|
1109
1129
|
case join
|
1110
|
-
when String
|
1111
|
-
:string_join
|
1112
1130
|
when Hash, Symbol, Array
|
1113
|
-
:association_join
|
1131
|
+
buckets[:association_join] << join
|
1114
1132
|
when ActiveRecord::Associations::JoinDependency
|
1115
|
-
:stashed_join
|
1133
|
+
buckets[:stashed_join] << join
|
1116
1134
|
when Arel::Nodes::Join
|
1117
|
-
:join_node
|
1135
|
+
buckets[:join_node] << join
|
1118
1136
|
else
|
1119
1137
|
raise "unknown class: %s" % join.class.name
|
1120
1138
|
end
|
@@ -1124,25 +1142,21 @@ module ActiveRecord
|
|
1124
1142
|
end
|
1125
1143
|
|
1126
1144
|
def build_join_query(manager, buckets, join_type, aliases)
|
1127
|
-
buckets.default = []
|
1128
|
-
|
1129
1145
|
association_joins = buckets[:association_join]
|
1130
1146
|
stashed_joins = buckets[:stashed_join]
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
string_joins.map! { |join| table.create_string_join(Arel.sql(join)) }
|
1147
|
+
leading_joins = buckets[:leading_join]
|
1148
|
+
join_nodes = buckets[:join_node]
|
1135
1149
|
|
1136
1150
|
join_sources = manager.join_sources
|
1137
|
-
join_sources.concat(
|
1151
|
+
join_sources.concat(leading_joins) unless leading_joins.empty?
|
1138
1152
|
|
1139
1153
|
unless association_joins.empty? && stashed_joins.empty?
|
1140
|
-
alias_tracker = alias_tracker(
|
1154
|
+
alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
|
1141
1155
|
join_dependency = construct_join_dependency(association_joins, join_type)
|
1142
1156
|
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
|
1143
1157
|
end
|
1144
1158
|
|
1145
|
-
join_sources.concat(
|
1159
|
+
join_sources.concat(join_nodes) unless join_nodes.empty?
|
1146
1160
|
end
|
1147
1161
|
|
1148
1162
|
def build_select(arel)
|
@@ -154,6 +154,8 @@ module ActiveRecord
|
|
154
154
|
end
|
155
155
|
|
156
156
|
def for_each(databases)
|
157
|
+
return {} unless defined?(Rails)
|
158
|
+
|
157
159
|
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
|
158
160
|
|
159
161
|
# if this is a single database application we don't want tasks for each primary database
|
@@ -331,10 +333,39 @@ module ActiveRecord
|
|
331
333
|
end
|
332
334
|
ActiveRecord::InternalMetadata.create_table
|
333
335
|
ActiveRecord::InternalMetadata[:environment] = environment
|
336
|
+
ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
|
334
337
|
ensure
|
335
338
|
Migration.verbose = verbose_was
|
336
339
|
end
|
337
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
|
+
|
338
369
|
def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
|
339
370
|
require "active_record/schema_dumper"
|
340
371
|
filename = dump_filename(spec_name, format)
|
@@ -466,6 +497,10 @@ module ActiveRecord
|
|
466
497
|
def local_database?(configuration)
|
467
498
|
configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
|
468
499
|
end
|
500
|
+
|
501
|
+
def schema_sha1(file)
|
502
|
+
Digest::SHA1.hexdigest(File.read(file))
|
503
|
+
end
|
469
504
|
end
|
470
505
|
end
|
471
506
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
connection.create_database configuration["database"], creation_options
|
17
17
|
establish_connection configuration
|
18
18
|
rescue ActiveRecord::StatementInvalid => error
|
19
|
-
if error.cause
|
19
|
+
if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
|
20
20
|
raise DatabaseAlreadyExists
|
21
21
|
else
|
22
22
|
raise
|
@@ -8,31 +8,16 @@ module ActiveRecord
|
|
8
8
|
create_and_load_schema(i, env_name: Rails.env)
|
9
9
|
end
|
10
10
|
|
11
|
-
ActiveSupport::Testing::Parallelization.run_cleanup_hook do
|
12
|
-
drop(env_name: Rails.env)
|
13
|
-
end
|
14
|
-
|
15
11
|
def self.create_and_load_schema(i, env_name:)
|
16
12
|
old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
|
17
13
|
|
18
14
|
ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
|
19
15
|
db_config.config["database"] += "-#{i}"
|
20
|
-
ActiveRecord::Tasks::DatabaseTasks.
|
21
|
-
ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
|
16
|
+
ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
|
22
17
|
end
|
23
18
|
ensure
|
24
19
|
ActiveRecord::Base.establish_connection(Rails.env.to_sym)
|
25
20
|
ENV["VERBOSE"] = old
|
26
21
|
end
|
27
|
-
|
28
|
-
def self.drop(env_name:)
|
29
|
-
old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
|
30
|
-
|
31
|
-
ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
|
32
|
-
ActiveRecord::Tasks::DatabaseTasks.drop(db_config.config)
|
33
|
-
end
|
34
|
-
ensure
|
35
|
-
ENV["VERBOSE"] = old
|
36
|
-
end
|
37
22
|
end
|
38
23
|
end
|
@@ -40,6 +40,7 @@ module ActiveRecord
|
|
40
40
|
include ActiveModel::Validations
|
41
41
|
|
42
42
|
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
|
43
|
+
# The validation context can be changed by passing <tt>context: context</tt>.
|
43
44
|
# The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
|
44
45
|
# with this when the validations module is mixed in, which it is by default.
|
45
46
|
def save(options = {})
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0.0
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.0.0
|
19
|
+
version: 6.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.0.0
|
26
|
+
version: 6.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 6.0.0
|
33
|
+
version: 6.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 6.0.0
|
40
|
+
version: 6.0.0
|
41
41
|
description: Databases on Rails. Build a persistent domain model by mapping database
|
42
42
|
tables to Ruby classes. Strong conventions for associations, validations, aggregations,
|
43
43
|
migrations, and testing come baked-in.
|
@@ -389,8 +389,8 @@ homepage: https://rubyonrails.org
|
|
389
389
|
licenses:
|
390
390
|
- MIT
|
391
391
|
metadata:
|
392
|
-
source_code_uri: https://github.com/rails/rails/tree/v6.0.0
|
393
|
-
changelog_uri: https://github.com/rails/rails/blob/v6.0.0
|
392
|
+
source_code_uri: https://github.com/rails/rails/tree/v6.0.0/activerecord
|
393
|
+
changelog_uri: https://github.com/rails/rails/blob/v6.0.0/activerecord/CHANGELOG.md
|
394
394
|
post_install_message:
|
395
395
|
rdoc_options:
|
396
396
|
- "--main"
|
@@ -404,9 +404,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
404
404
|
version: 2.5.0
|
405
405
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
406
406
|
requirements:
|
407
|
-
- - "
|
407
|
+
- - ">="
|
408
408
|
- !ruby/object:Gem::Version
|
409
|
-
version:
|
409
|
+
version: '0'
|
410
410
|
requirements: []
|
411
411
|
rubygems_version: 3.0.1
|
412
412
|
signing_key:
|