activerecord 6.0.3.6 → 6.0.4.2
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 +123 -0
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +2 -3
- data/lib/active_record/associations/association_scope.rb +7 -1
- data/lib/active_record/associations/collection_association.rb +7 -0
- data/lib/active_record/associations/collection_proxy.rb +1 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +14 -6
- data/lib/active_record/associations/preloader/association.rb +41 -23
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +6 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/autosave_association.rb +10 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +6 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +9 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -1
- data/lib/active_record/core.rb +5 -6
- data/lib/active_record/enum.rb +13 -6
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +9 -0
- data/lib/active_record/model_schema.rb +29 -0
- data/lib/active_record/reflection.rb +11 -13
- data/lib/active_record/relation/calculations.rb +1 -1
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +1 -1
- data/lib/active_record/relation/merger.rb +7 -2
- data/lib/active_record/relation/query_methods.rb +23 -11
- data/lib/active_record/relation.rb +6 -3
- data/lib/active_record/scoping/named.rb +5 -0
- data/lib/active_record/test_fixtures.rb +19 -1
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/validations/associated.rb +1 -1
- metadata +13 -13
@@ -67,15 +67,34 @@ module ActiveRecord
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def extract_bounds(value)
|
70
|
-
from, to = value[1..-2].split(",")
|
70
|
+
from, to = value[1..-2].split(",", 2)
|
71
71
|
{
|
72
|
-
from: (
|
73
|
-
to: (
|
74
|
-
exclude_start: (
|
75
|
-
exclude_end: (
|
72
|
+
from: (from == "" || from == "-infinity") ? infinity(negative: true) : unquote(from),
|
73
|
+
to: (to == "" || to == "infinity") ? infinity : unquote(to),
|
74
|
+
exclude_start: value.start_with?("("),
|
75
|
+
exclude_end: value.end_with?(")")
|
76
76
|
}
|
77
77
|
end
|
78
78
|
|
79
|
+
# When formatting the bound values of range types, PostgreSQL quotes
|
80
|
+
# the bound value using double-quotes in certain conditions. Within
|
81
|
+
# a double-quoted string, literal " and \ characters are themselves
|
82
|
+
# escaped. In input, PostgreSQL accepts multiple escape styles for "
|
83
|
+
# (either \" or "") but in output always uses "".
|
84
|
+
# See:
|
85
|
+
# * https://www.postgresql.org/docs/current/rangetypes.html#RANGETYPES-IO
|
86
|
+
# * https://www.postgresql.org/docs/current/rowtypes.html#ROWTYPES-IO-SYNTAX
|
87
|
+
def unquote(value)
|
88
|
+
if value.start_with?('"') && value.end_with?('"')
|
89
|
+
unquoted_value = value[1..-2]
|
90
|
+
unquoted_value.gsub!('""', '"')
|
91
|
+
unquoted_value.gsub!('\\\\', '\\')
|
92
|
+
unquoted_value
|
93
|
+
else
|
94
|
+
value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
79
98
|
def infinity(negative: false)
|
80
99
|
if subtype.respond_to?(:infinity)
|
81
100
|
subtype.infinity(negative: negative)
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
module SQLite3
|
6
6
|
module DatabaseStatements
|
7
7
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
8
|
-
:
|
8
|
+
:pragma
|
9
9
|
) # :nodoc:
|
10
10
|
private_constant :READ_QUERY
|
11
11
|
|
@@ -61,7 +61,7 @@ module ActiveRecord
|
|
61
61
|
|
62
62
|
def remove_foreign_key(from_table, to_table = nil, **options)
|
63
63
|
to_table ||= options[:to_table]
|
64
|
-
options = options.except(:name, :to_table)
|
64
|
+
options = options.except(:name, :to_table, :validate)
|
65
65
|
foreign_keys = foreign_keys(from_table)
|
66
66
|
|
67
67
|
fkey = foreign_keys.detect do |fk|
|
@@ -283,13 +283,14 @@ module ActiveRecord
|
|
283
283
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
284
284
|
alter_table(table_name) do |definition|
|
285
285
|
definition[column_name].instance_eval do
|
286
|
-
self.type = type
|
286
|
+
self.type = aliased_types(type.to_s, type)
|
287
287
|
self.limit = options[:limit] if options.include?(:limit)
|
288
288
|
self.default = options[:default] if options.include?(:default)
|
289
289
|
self.null = options[:null] if options.include?(:null)
|
290
290
|
self.precision = options[:precision] if options.include?(:precision)
|
291
291
|
self.scale = options[:scale] if options.include?(:scale)
|
292
292
|
self.collation = options[:collation] if options.include?(:collation)
|
293
|
+
self.options.merge!(options)
|
293
294
|
end
|
294
295
|
end
|
295
296
|
end
|
data/lib/active_record/core.rb
CHANGED
@@ -285,18 +285,17 @@ module ActiveRecord
|
|
285
285
|
false
|
286
286
|
end
|
287
287
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
end
|
288
|
+
def cached_find_by_statement(key, &block) # :nodoc:
|
289
|
+
cache = @find_by_statement_cache[connection.prepared_statements]
|
290
|
+
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
291
|
+
end
|
293
292
|
|
293
|
+
private
|
294
294
|
def relation
|
295
295
|
relation = Relation.create(self)
|
296
296
|
|
297
297
|
if finder_needs_type_condition? && !ignore_default_scope?
|
298
298
|
relation.where!(type_condition)
|
299
|
-
relation.create_with!(inheritance_column.to_s => sti_name)
|
300
299
|
else
|
301
300
|
relation
|
302
301
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -173,6 +173,7 @@ module ActiveRecord
|
|
173
173
|
|
174
174
|
_enum_methods_module.module_eval do
|
175
175
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
176
|
+
value_method_names = []
|
176
177
|
pairs.each do |label, value|
|
177
178
|
if enum_prefix == true
|
178
179
|
prefix = "#{name}_"
|
@@ -186,6 +187,7 @@ module ActiveRecord
|
|
186
187
|
end
|
187
188
|
|
188
189
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
190
|
+
value_method_names << value_method_name
|
189
191
|
enum_values[label] = value
|
190
192
|
label = label.to_s
|
191
193
|
|
@@ -200,8 +202,6 @@ module ActiveRecord
|
|
200
202
|
# scope :active, -> { where(status: 0) }
|
201
203
|
# scope :not_active, -> { where.not(status: 0) }
|
202
204
|
if enum_scopes != false
|
203
|
-
klass.send(:detect_negative_condition!, value_method_name)
|
204
|
-
|
205
205
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
206
206
|
klass.scope value_method_name, -> { where(attr => value) }
|
207
207
|
|
@@ -209,6 +209,7 @@ module ActiveRecord
|
|
209
209
|
klass.scope "not_#{value_method_name}", -> { where.not(attr => value) }
|
210
210
|
end
|
211
211
|
end
|
212
|
+
klass.send(:detect_negative_enum_conditions!, value_method_names) if enum_scopes != false
|
212
213
|
end
|
213
214
|
enum_values.freeze
|
214
215
|
end
|
@@ -264,10 +265,16 @@ module ActiveRecord
|
|
264
265
|
}
|
265
266
|
end
|
266
267
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
268
|
+
def detect_negative_enum_conditions!(method_names)
|
269
|
+
return unless logger
|
270
|
+
|
271
|
+
method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
|
272
|
+
inverted_form = potential_not.sub("not_", "")
|
273
|
+
if method_names.include?(inverted_form)
|
274
|
+
logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
|
275
|
+
" This has caused a conflict with auto generated negative scopes." \
|
276
|
+
" Avoid using enum elements starting with 'not' where the positive form is also an element."
|
277
|
+
end
|
271
278
|
end
|
272
279
|
end
|
273
280
|
end
|
@@ -60,6 +60,15 @@ module ActiveRecord
|
|
60
60
|
self.class.locking_enabled?
|
61
61
|
end
|
62
62
|
|
63
|
+
def increment!(*, **) #:nodoc:
|
64
|
+
super.tap do
|
65
|
+
if locking_enabled?
|
66
|
+
self[self.class.locking_column] += 1
|
67
|
+
clear_attribute_change(self.class.locking_column)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
63
72
|
private
|
64
73
|
def _create_record(attribute_names = self.attribute_names)
|
65
74
|
if locking_enabled?
|
@@ -287,6 +287,35 @@ module ActiveRecord
|
|
287
287
|
|
288
288
|
# Sets the columns names the model should ignore. Ignored columns won't have attribute
|
289
289
|
# accessors defined, and won't be referenced in SQL queries.
|
290
|
+
#
|
291
|
+
# A common usage pattern for this method is to ensure all references to an attribute
|
292
|
+
# have been removed and deployed, before a migration to drop the column from the database
|
293
|
+
# has been deployed and run. Using this two step approach to dropping columns ensures there
|
294
|
+
# is no code that raises errors due to having a cached schema in memory at the time the
|
295
|
+
# schema migration is run.
|
296
|
+
#
|
297
|
+
# For example, given a model where you want to drop the "category" attribute, first mark it
|
298
|
+
# as ignored:
|
299
|
+
#
|
300
|
+
# class Project < ActiveRecord::Base
|
301
|
+
# # schema:
|
302
|
+
# # id :bigint
|
303
|
+
# # name :string, limit: 255
|
304
|
+
# # category :string, limit: 255
|
305
|
+
#
|
306
|
+
# self.ignored_columns = [:category]
|
307
|
+
# end
|
308
|
+
#
|
309
|
+
# The schema still contains `category`, but now the model omits it, so any meta-driven code or
|
310
|
+
# schema caching will not attempt to use the column:
|
311
|
+
#
|
312
|
+
# Project.columns_hash["category"] => nil
|
313
|
+
#
|
314
|
+
# You will get an error if accessing that attribute directly, so ensure all usages of the
|
315
|
+
# column are removed (automated tests can help you find any usages).
|
316
|
+
#
|
317
|
+
# user = Project.create!(name: "First Project")
|
318
|
+
# user.category # => raises NoMethodError
|
290
319
|
def ignored_columns=(columns)
|
291
320
|
@ignored_columns = columns.map(&:to_s)
|
292
321
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/string/filters"
|
4
|
-
require "concurrent/map"
|
5
4
|
|
6
5
|
module ActiveRecord
|
7
6
|
# = Active Record Reflection
|
@@ -201,9 +200,9 @@ module ActiveRecord
|
|
201
200
|
klass_scope
|
202
201
|
end
|
203
202
|
|
204
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
203
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
205
204
|
if scope
|
206
|
-
[scope_for(build_scope(table, predicate_builder))]
|
205
|
+
[scope_for(build_scope(table, predicate_builder, klass))]
|
207
206
|
else
|
208
207
|
[]
|
209
208
|
end
|
@@ -292,7 +291,7 @@ module ActiveRecord
|
|
292
291
|
JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
|
293
292
|
end
|
294
293
|
|
295
|
-
def build_scope(table, predicate_builder = predicate_builder(table))
|
294
|
+
def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
|
296
295
|
Relation.create(
|
297
296
|
klass,
|
298
297
|
table: table,
|
@@ -432,19 +431,18 @@ module ActiveRecord
|
|
432
431
|
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
433
432
|
@foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
|
434
433
|
@constructable = calculate_constructable(macro, options)
|
435
|
-
@association_scope_cache = Concurrent::Map.new
|
436
434
|
|
437
435
|
if options[:class_name] && options[:class_name].class == Class
|
438
436
|
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
439
437
|
end
|
440
438
|
end
|
441
439
|
|
442
|
-
def association_scope_cache(
|
443
|
-
key =
|
440
|
+
def association_scope_cache(klass, owner, &block)
|
441
|
+
key = self
|
444
442
|
if polymorphic?
|
445
443
|
key = [key, owner._read_attribute(@foreign_type)]
|
446
444
|
end
|
447
|
-
|
445
|
+
klass.cached_find_by_statement(key, &block)
|
448
446
|
end
|
449
447
|
|
450
448
|
def constructable? # :nodoc:
|
@@ -510,7 +508,7 @@ module ActiveRecord
|
|
510
508
|
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
511
509
|
# SQL queries on associations.
|
512
510
|
def clear_association_scope_cache # :nodoc:
|
513
|
-
|
511
|
+
klass.initialize_find_by_cache
|
514
512
|
end
|
515
513
|
|
516
514
|
def nested?
|
@@ -839,8 +837,8 @@ module ActiveRecord
|
|
839
837
|
source_reflection.scopes + super
|
840
838
|
end
|
841
839
|
|
842
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
843
|
-
source_reflection.join_scopes(table, predicate_builder) + super
|
840
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
841
|
+
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
844
842
|
end
|
845
843
|
|
846
844
|
def has_scope?
|
@@ -1003,9 +1001,9 @@ module ActiveRecord
|
|
1003
1001
|
@previous_reflection = previous_reflection
|
1004
1002
|
end
|
1005
1003
|
|
1006
|
-
def join_scopes(table, predicate_builder) # :nodoc:
|
1004
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
1007
1005
|
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1008
|
-
scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
|
1006
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
|
1009
1007
|
end
|
1010
1008
|
|
1011
1009
|
def constraints
|
@@ -134,7 +134,7 @@ module ActiveRecord
|
|
134
134
|
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
135
135
|
end
|
136
136
|
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
137
|
-
relation.order_values = []
|
137
|
+
relation.order_values = [] if group_values.empty?
|
138
138
|
end
|
139
139
|
|
140
140
|
relation.calculate(operation, column_name)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "mutex_m"
|
4
|
+
require "active_support/core_ext/module/delegation"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module Delegation # :nodoc:
|
@@ -59,7 +60,7 @@ module ActiveRecord
|
|
59
60
|
synchronize do
|
60
61
|
return if method_defined?(method)
|
61
62
|
|
62
|
-
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
|
63
|
+
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
|
63
64
|
definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
|
64
65
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
65
66
|
def #{method}(#{definition})
|
@@ -371,7 +371,7 @@ module ActiveRecord
|
|
371
371
|
|
372
372
|
def apply_join_dependency(eager_loading: group_values.empty?)
|
373
373
|
join_dependency = construct_join_dependency(
|
374
|
-
eager_load_values
|
374
|
+
eager_load_values | includes_values, Arel::Nodes::OuterJoin
|
375
375
|
)
|
376
376
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
377
377
|
|
@@ -135,11 +135,16 @@ module ActiveRecord
|
|
135
135
|
if other.klass == relation.klass
|
136
136
|
relation.left_outer_joins!(*other.left_outer_joins_values)
|
137
137
|
else
|
138
|
-
associations = other.left_outer_joins_values
|
138
|
+
associations, others = other.left_outer_joins_values.partition do |join|
|
139
|
+
case join
|
140
|
+
when Hash, Symbol, Array; true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
139
144
|
join_dependency = other.construct_join_dependency(
|
140
145
|
associations, Arel::Nodes::OuterJoin
|
141
146
|
)
|
142
|
-
relation.
|
147
|
+
relation.left_outer_joins!(join_dependency, *others)
|
143
148
|
end
|
144
149
|
end
|
145
150
|
|
@@ -331,7 +331,7 @@ module ActiveRecord
|
|
331
331
|
def group!(*args) # :nodoc:
|
332
332
|
args.flatten!
|
333
333
|
|
334
|
-
self.group_values
|
334
|
+
self.group_values += args
|
335
335
|
self
|
336
336
|
end
|
337
337
|
|
@@ -1094,12 +1094,14 @@ module ActiveRecord
|
|
1094
1094
|
end
|
1095
1095
|
end
|
1096
1096
|
|
1097
|
-
def select_association_list(associations)
|
1097
|
+
def select_association_list(associations, stashed_joins = nil)
|
1098
1098
|
result = []
|
1099
1099
|
associations.each do |association|
|
1100
1100
|
case association
|
1101
1101
|
when Hash, Symbol, Array
|
1102
1102
|
result << association
|
1103
|
+
when ActiveRecord::Associations::JoinDependency
|
1104
|
+
stashed_joins&.<< association
|
1103
1105
|
else
|
1104
1106
|
yield if block_given?
|
1105
1107
|
end
|
@@ -1107,28 +1109,32 @@ module ActiveRecord
|
|
1107
1109
|
result
|
1108
1110
|
end
|
1109
1111
|
|
1110
|
-
def valid_association_list(associations)
|
1111
|
-
select_association_list(associations) do
|
1112
|
+
def valid_association_list(associations, stashed_joins)
|
1113
|
+
select_association_list(associations, stashed_joins) do
|
1112
1114
|
raise ArgumentError, "only Hash, Symbol and Array are allowed"
|
1113
1115
|
end
|
1114
1116
|
end
|
1115
1117
|
|
1116
1118
|
def build_left_outer_joins(manager, outer_joins, aliases)
|
1117
1119
|
buckets = Hash.new { |h, k| h[k] = [] }
|
1118
|
-
buckets[:association_join] = valid_association_list(outer_joins)
|
1120
|
+
buckets[:association_join] = valid_association_list(outer_joins, buckets[:stashed_join])
|
1119
1121
|
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
|
1120
1122
|
end
|
1121
1123
|
|
1124
|
+
class ::Arel::Nodes::LeadingJoin < Arel::Nodes::InnerJoin # :nodoc:
|
1125
|
+
end
|
1126
|
+
|
1122
1127
|
def build_joins(manager, joins, aliases)
|
1123
1128
|
buckets = Hash.new { |h, k| h[k] = [] }
|
1124
1129
|
|
1125
1130
|
unless left_outer_joins_values.empty?
|
1126
|
-
|
1127
|
-
|
1131
|
+
stashed_left_joins = []
|
1132
|
+
left_joins = valid_association_list(left_outer_joins_values.flatten, stashed_left_joins)
|
1133
|
+
stashed_left_joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
|
1128
1134
|
end
|
1129
1135
|
|
1130
1136
|
if joins.last.is_a?(ActiveRecord::Associations::JoinDependency)
|
1131
|
-
|
1137
|
+
stashed_eager_load = joins.pop if joins.last.base_klass == klass
|
1132
1138
|
end
|
1133
1139
|
|
1134
1140
|
joins.map! do |join|
|
@@ -1141,7 +1147,7 @@ module ActiveRecord
|
|
1141
1147
|
|
1142
1148
|
while joins.first.is_a?(Arel::Nodes::Join)
|
1143
1149
|
join_node = joins.shift
|
1144
|
-
if join_node.is_a?(Arel::Nodes::
|
1150
|
+
if !join_node.is_a?(Arel::Nodes::LeadingJoin) && (stashed_eager_load || stashed_left_joins)
|
1145
1151
|
buckets[:join_node] << join_node
|
1146
1152
|
else
|
1147
1153
|
buckets[:leading_join] << join_node
|
@@ -1161,6 +1167,9 @@ module ActiveRecord
|
|
1161
1167
|
end
|
1162
1168
|
end
|
1163
1169
|
|
1170
|
+
buckets[:stashed_join].concat stashed_left_joins if stashed_left_joins
|
1171
|
+
buckets[:stashed_join] << stashed_eager_load if stashed_eager_load
|
1172
|
+
|
1164
1173
|
build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
|
1165
1174
|
end
|
1166
1175
|
|
@@ -1356,12 +1365,15 @@ module ActiveRecord
|
|
1356
1365
|
end
|
1357
1366
|
end
|
1358
1367
|
|
1359
|
-
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references]
|
1368
|
+
STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope, :references, :annotate, :optimizer_hints]
|
1360
1369
|
def structurally_incompatible_values_for_or(other)
|
1361
1370
|
values = other.values
|
1362
1371
|
STRUCTURAL_OR_METHODS.reject do |method|
|
1363
1372
|
default = DEFAULT_VALUES[method]
|
1364
|
-
@values.fetch(method, default)
|
1373
|
+
v1, v2 = @values.fetch(method, default), values.fetch(method, default)
|
1374
|
+
v1 = v1.uniq if v1.is_a?(Array)
|
1375
|
+
v2 = v2.uniq if v2.is_a?(Array)
|
1376
|
+
v1 == v2
|
1365
1377
|
end
|
1366
1378
|
end
|
1367
1379
|
|
@@ -497,8 +497,8 @@ module ActiveRecord
|
|
497
497
|
update_all updates
|
498
498
|
end
|
499
499
|
|
500
|
-
# Touches all records in the current relation
|
501
|
-
#
|
500
|
+
# Touches all records in the current relation, setting the +updated_at+/+updated_on+ attributes to the current time or the time specified.
|
501
|
+
# It does not instantiate the involved models, and it does not trigger Active Record callbacks or validations.
|
502
502
|
# This method can be passed attribute names and an optional time argument.
|
503
503
|
# If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
|
504
504
|
# If no time argument is passed, the current time is used as default.
|
@@ -671,7 +671,10 @@ module ActiveRecord
|
|
671
671
|
end
|
672
672
|
|
673
673
|
def scope_for_create
|
674
|
-
where_values_hash
|
674
|
+
hash = where_values_hash
|
675
|
+
hash.delete(klass.inheritance_column) if klass.finder_needs_type_condition?
|
676
|
+
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
677
|
+
hash
|
675
678
|
end
|
676
679
|
|
677
680
|
# Returns true if relation needs eager loading.
|
@@ -200,11 +200,16 @@ module ActiveRecord
|
|
200
200
|
scope
|
201
201
|
end
|
202
202
|
end
|
203
|
+
singleton_class.send(:ruby2_keywords, name) if respond_to?(:ruby2_keywords, true)
|
203
204
|
|
204
205
|
generate_relation_method(name)
|
205
206
|
end
|
206
207
|
|
207
208
|
private
|
209
|
+
def singleton_method_added(name)
|
210
|
+
generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
|
211
|
+
end
|
212
|
+
|
208
213
|
def valid_scope_name?(name)
|
209
214
|
if respond_to?(name, true) && logger
|
210
215
|
logger.warn "Creating scope :#{name}. " \
|
@@ -112,6 +112,8 @@ module ActiveRecord
|
|
112
112
|
|
113
113
|
# Load fixtures once and begin transaction.
|
114
114
|
if run_in_transaction?
|
115
|
+
@saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
|
116
|
+
|
115
117
|
if @@already_loaded_fixtures[self.class]
|
116
118
|
@loaded_fixtures = @@already_loaded_fixtures[self.class]
|
117
119
|
else
|
@@ -166,6 +168,7 @@ module ActiveRecord
|
|
166
168
|
connection.pool.lock_thread = false
|
167
169
|
end
|
168
170
|
@fixture_connections.clear
|
171
|
+
teardown_shared_connection_pool
|
169
172
|
else
|
170
173
|
ActiveRecord::FixtureSet.reset_cache
|
171
174
|
end
|
@@ -187,7 +190,7 @@ module ActiveRecord
|
|
187
190
|
# need to share a connection pool so that the reading connection
|
188
191
|
# can see data in the open transaction on the writing connection.
|
189
192
|
def setup_shared_connection_pool
|
190
|
-
writing_handler = ActiveRecord::Base.
|
193
|
+
writing_handler = ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role]
|
191
194
|
|
192
195
|
ActiveRecord::Base.connection_handlers.values.each do |handler|
|
193
196
|
if handler != writing_handler
|
@@ -195,12 +198,27 @@ module ActiveRecord
|
|
195
198
|
name = pool.spec.name
|
196
199
|
writing_connection = writing_handler.retrieve_connection_pool(name)
|
197
200
|
return unless writing_connection
|
201
|
+
|
202
|
+
reading_connection = handler.send(:owner_to_pool)[name]
|
203
|
+
next if reading_connection == writing_connection
|
204
|
+
|
205
|
+
@saved_pool_configs[handler][name] = reading_connection
|
198
206
|
handler.send(:owner_to_pool)[name] = writing_connection
|
199
207
|
end
|
200
208
|
end
|
201
209
|
end
|
202
210
|
end
|
203
211
|
|
212
|
+
def teardown_shared_connection_pool
|
213
|
+
@saved_pool_configs.each_pair do |handler, pools|
|
214
|
+
pools.each_pair do |name, pool|
|
215
|
+
handler.send(:owner_to_pool)[name] = pool
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
@saved_pool_configs.clear
|
220
|
+
end
|
221
|
+
|
204
222
|
def load_fixtures(config)
|
205
223
|
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
|
206
224
|
Hash[fixtures.map { |f| [f.name, f] }]
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
|
6
6
|
def validate_each(record, attribute, value)
|
7
7
|
if Array(value).reject { |r| valid_object?(r) }.any?
|
8
|
-
record.errors.add(attribute, :invalid,
|
8
|
+
record.errors.add(attribute, :invalid, options.merge(value: value))
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|