activerecord 6.0.0.rc2 → 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 +101 -3
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +1 -1
- data/lib/active_record/associations/association.rb +9 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +1 -1
- data/lib/active_record/autosave_association.rb +7 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +25 -10
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +4 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +13 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +3 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -0
- data/lib/active_record/connection_handling.rb +11 -4
- data/lib/active_record/core.rb +8 -4
- 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 +2 -2
- data/lib/active_record/insert_all.rb +2 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +13 -5
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/railties/databases.rake +6 -3
- data/lib/active_record/relation.rb +1 -0
- data/lib/active_record/relation/calculations.rb +1 -3
- data/lib/active_record/relation/finder_methods.rb +10 -1
- data/lib/active_record/relation/query_methods.rb +47 -21
- 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/transactions.rb +1 -1
- data/lib/active_record/validations.rb +1 -0
- data/lib/arel.rb +12 -5
- metadata +12 -9
@@ -110,6 +110,14 @@ module ActiveRecord
|
|
110
110
|
!mariadb? && database_version >= "5.7.7"
|
111
111
|
end
|
112
112
|
|
113
|
+
def supports_common_table_expressions?
|
114
|
+
if mariadb?
|
115
|
+
database_version >= "10.2.1"
|
116
|
+
else
|
117
|
+
database_version >= "8.0.1"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
113
121
|
def supports_advisory_locks?
|
114
122
|
true
|
115
123
|
end
|
@@ -440,11 +448,11 @@ module ActiveRecord
|
|
440
448
|
|
441
449
|
query_values(<<~SQL, "SCHEMA")
|
442
450
|
SELECT column_name
|
443
|
-
FROM information_schema.
|
444
|
-
WHERE
|
451
|
+
FROM information_schema.statistics
|
452
|
+
WHERE index_name = 'PRIMARY'
|
445
453
|
AND table_schema = #{scope[:schema]}
|
446
454
|
AND table_name = #{scope[:name]}
|
447
|
-
ORDER BY
|
455
|
+
ORDER BY seq_in_index
|
448
456
|
SQL
|
449
457
|
end
|
450
458
|
|
@@ -581,6 +589,7 @@ module ActiveRecord
|
|
581
589
|
end
|
582
590
|
|
583
591
|
# See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
|
592
|
+
ER_FILSORT_ABORT = 1028
|
584
593
|
ER_DUP_ENTRY = 1062
|
585
594
|
ER_NOT_NULL_VIOLATION = 1048
|
586
595
|
ER_NO_REFERENCED_ROW = 1216
|
@@ -622,7 +631,7 @@ module ActiveRecord
|
|
622
631
|
Deadlocked.new(message, sql: sql, binds: binds)
|
623
632
|
when ER_LOCK_WAIT_TIMEOUT
|
624
633
|
LockWaitTimeout.new(message, sql: sql, binds: binds)
|
625
|
-
when ER_QUERY_TIMEOUT
|
634
|
+
when ER_QUERY_TIMEOUT, ER_FILSORT_ABORT
|
626
635
|
StatementTimeout.new(message, sql: sql, binds: binds)
|
627
636
|
when ER_QUERY_INTERRUPTED
|
628
637
|
QueryCanceled.new(message, sql: sql, binds: binds)
|
@@ -186,7 +186,7 @@ module ActiveRecord
|
|
186
186
|
adapter_method = "#{spec[:adapter]}_connection"
|
187
187
|
|
188
188
|
unless ActiveRecord::Base.respond_to?(adapter_method)
|
189
|
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec
|
189
|
+
raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
|
190
190
|
end
|
191
191
|
|
192
192
|
ConnectionSpecification.new(spec.delete(:name) || "primary", spec, adapter_method)
|
@@ -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
|
|
@@ -19,7 +19,9 @@ module ActiveRecord
|
|
19
19
|
execute(sql, name).to_a
|
20
20
|
end
|
21
21
|
|
22
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
22
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
23
|
+
:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :describe, :desc, :with
|
24
|
+
) # :nodoc:
|
23
25
|
private_constant :READ_QUERY
|
24
26
|
|
25
27
|
def write_query?(sql) # :nodoc:
|
@@ -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
|
|
@@ -67,7 +67,9 @@ module ActiveRecord
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
70
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
71
|
+
:begin, :commit, :explain, :select, :set, :show, :release, :savepoint, :rollback, :with
|
72
|
+
) # :nodoc:
|
71
73
|
private_constant :READ_QUERY
|
72
74
|
|
73
75
|
def write_query?(sql) # :nodoc:
|
@@ -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
|
|
@@ -4,7 +4,9 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
6
|
module DatabaseStatements
|
7
|
-
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
7
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
8
|
+
:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback, :with
|
9
|
+
) # :nodoc:
|
8
10
|
private_constant :READ_QUERY
|
9
11
|
|
10
12
|
def write_query?(sql) # :nodoc:
|
@@ -113,8 +113,9 @@ module ActiveRecord
|
|
113
113
|
# Dog.run_a_long_query
|
114
114
|
# end
|
115
115
|
#
|
116
|
-
# When using the database key a new connection will be established every time.
|
117
|
-
|
116
|
+
# When using the database key a new connection will be established every time. It is not
|
117
|
+
# recommended to use this outside of one-off scripts.
|
118
|
+
def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
|
118
119
|
if database && role
|
119
120
|
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
120
121
|
elsif database
|
@@ -130,7 +131,13 @@ module ActiveRecord
|
|
130
131
|
|
131
132
|
with_handler(role, &blk)
|
132
133
|
elsif role
|
133
|
-
|
134
|
+
if role == writing_role
|
135
|
+
with_handler(role.to_sym) do
|
136
|
+
connection_handler.while_preventing_writes(prevent_writes, &blk)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
with_handler(role.to_sym, &blk)
|
140
|
+
end
|
134
141
|
else
|
135
142
|
raise ArgumentError, "must provide a `database` or a `role`."
|
136
143
|
end
|
@@ -204,7 +211,7 @@ module ActiveRecord
|
|
204
211
|
# Return the specification name from the current class or its parent.
|
205
212
|
def connection_specification_name
|
206
213
|
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
207
|
-
return
|
214
|
+
return self == Base ? "primary" : superclass.connection_specification_name
|
208
215
|
end
|
209
216
|
@connection_specification_name
|
210
217
|
end
|
data/lib/active_record/core.rb
CHANGED
@@ -586,12 +586,16 @@ module ActiveRecord
|
|
586
586
|
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
|
587
587
|
end
|
588
588
|
|
589
|
+
class InspectionMask < DelegateClass(::String)
|
590
|
+
def pretty_print(pp)
|
591
|
+
pp.text __getobj__
|
592
|
+
end
|
593
|
+
end
|
594
|
+
private_constant :InspectionMask
|
595
|
+
|
589
596
|
def inspection_filter
|
590
597
|
@inspection_filter ||= begin
|
591
|
-
mask =
|
592
|
-
def mask.pretty_print(pp)
|
593
|
-
pp.text __getobj__
|
594
|
-
end
|
598
|
+
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
595
599
|
ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
|
596
600
|
end
|
597
601
|
end
|
@@ -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).
|
@@ -24,7 +24,7 @@ module ActiveRecord
|
|
24
24
|
message = +"#{model} "
|
25
25
|
message << "Bulk " if inserts.many?
|
26
26
|
message << (on_duplicate == :update ? "Upsert" : "Insert")
|
27
|
-
connection.
|
27
|
+
connection.exec_insert_all to_sql, message
|
28
28
|
end
|
29
29
|
|
30
30
|
def updatable_columns
|
@@ -110,8 +110,7 @@ module ActiveRecord
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
-
|
114
|
-
class Builder
|
113
|
+
class Builder # :nodoc:
|
115
114
|
attr_reader :model
|
116
115
|
|
117
116
|
delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
|