activerecord 5.2.2.1 → 5.2.5
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 +116 -0
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +4 -5
- data/lib/active_record/associations/collection_proxy.rb +8 -34
- data/lib/active_record/associations/has_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_through_association.rb +6 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +28 -7
- data/lib/active_record/associations/preloader.rb +1 -1
- data/lib/active_record/autosave_association.rb +20 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +33 -10
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -8
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -24
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -3
- data/lib/active_record/core.rb +2 -1
- data/lib/active_record/errors.rb +18 -12
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/migration/compatibility.rb +15 -15
- data/lib/active_record/persistence.rb +3 -1
- data/lib/active_record/querying.rb +1 -2
- data/lib/active_record/reflection.rb +10 -14
- data/lib/active_record/relation/calculations.rb +16 -12
- data/lib/active_record/relation/finder_methods.rb +6 -2
- data/lib/active_record/relation/merger.rb +6 -3
- data/lib/active_record/relation/predicate_builder.rb +14 -9
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +35 -10
- data/lib/active_record/scoping/default.rb +2 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/transactions.rb +1 -1
- metadata +9 -9
@@ -17,20 +17,18 @@ module ActiveRecord
|
|
17
17
|
|
18
18
|
class V5_1 < V5_2
|
19
19
|
def change_column(table_name, column_name, type, options = {})
|
20
|
-
if adapter_name == "PostgreSQL"
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
26
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
20
|
+
if connection.adapter_name == "PostgreSQL"
|
21
|
+
super(table_name, column_name, type, options.except(:default, :null, :comment))
|
22
|
+
connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
23
|
+
connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
24
|
+
connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
27
25
|
else
|
28
26
|
super
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
30
|
def create_table(table_name, options = {})
|
33
|
-
if adapter_name == "Mysql2"
|
31
|
+
if connection.adapter_name == "Mysql2"
|
34
32
|
super(table_name, options: "ENGINE=InnoDB", **options)
|
35
33
|
else
|
36
34
|
super
|
@@ -52,13 +50,13 @@ module ActiveRecord
|
|
52
50
|
end
|
53
51
|
|
54
52
|
def create_table(table_name, options = {})
|
55
|
-
if adapter_name == "PostgreSQL"
|
53
|
+
if connection.adapter_name == "PostgreSQL"
|
56
54
|
if options[:id] == :uuid && !options.key?(:default)
|
57
55
|
options[:default] = "uuid_generate_v4()"
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
61
|
-
unless adapter_name == "Mysql2" && options[:id] == :bigint
|
59
|
+
unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
|
62
60
|
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
|
63
61
|
options[:default] = nil
|
64
62
|
end
|
@@ -175,7 +173,7 @@ module ActiveRecord
|
|
175
173
|
if options[:name].present?
|
176
174
|
options[:name].to_s
|
177
175
|
else
|
178
|
-
index_name(table_name, column: column_names)
|
176
|
+
connection.index_name(table_name, column: column_names)
|
179
177
|
end
|
180
178
|
super
|
181
179
|
end
|
@@ -195,15 +193,17 @@ module ActiveRecord
|
|
195
193
|
end
|
196
194
|
|
197
195
|
def index_name_for_remove(table_name, options = {})
|
198
|
-
index_name = index_name(table_name, options)
|
196
|
+
index_name = connection.index_name(table_name, options)
|
199
197
|
|
200
|
-
unless index_name_exists?(table_name, index_name)
|
198
|
+
unless connection.index_name_exists?(table_name, index_name)
|
201
199
|
if options.is_a?(Hash) && options.has_key?(:name)
|
202
200
|
options_without_column = options.dup
|
203
201
|
options_without_column.delete :column
|
204
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
202
|
+
index_name_without_column = connection.index_name(table_name, options_without_column)
|
205
203
|
|
206
|
-
|
204
|
+
if connection.index_name_exists?(table_name, index_name_without_column)
|
205
|
+
return index_name_without_column
|
206
|
+
end
|
207
207
|
end
|
208
208
|
|
209
209
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
@@ -97,11 +97,13 @@ module ActiveRecord
|
|
97
97
|
# When running callbacks is not needed for each record update,
|
98
98
|
# it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
|
99
99
|
# for updating all records in a single query.
|
100
|
-
def update(id, attributes)
|
100
|
+
def update(id = :all, attributes)
|
101
101
|
if id.is_a?(Array)
|
102
102
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
103
103
|
object.update(attributes[idx])
|
104
104
|
}
|
105
|
+
elsif id == :all
|
106
|
+
all.each { |record| record.update(attributes) }
|
105
107
|
else
|
106
108
|
if ActiveRecord::Base === id
|
107
109
|
raise ArgumentError,
|
@@ -40,8 +40,7 @@ module ActiveRecord
|
|
40
40
|
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
41
41
|
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
|
42
42
|
column_types = result_set.column_types.dup
|
43
|
-
|
44
|
-
cached_columns_hash.each_key { |k| column_types.delete k }
|
43
|
+
attribute_types.each_key { |k| column_types.delete k }
|
45
44
|
message_bus = ActiveSupport::Notifications.instrumenter
|
46
45
|
|
47
46
|
payload = {
|
@@ -174,28 +174,24 @@ module ActiveRecord
|
|
174
174
|
scope ? [scope] : []
|
175
175
|
end
|
176
176
|
|
177
|
-
def
|
178
|
-
key = join_keys.key
|
179
|
-
foreign_key = join_keys.foreign_key
|
180
|
-
|
181
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
182
|
-
|
183
|
-
if klass.finder_needs_type_condition?
|
184
|
-
table.create_and([constraint, klass.send(:type_condition, table)])
|
185
|
-
else
|
186
|
-
constraint
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def join_scope(table, foreign_klass)
|
177
|
+
def join_scope(table, foreign_table, foreign_klass)
|
191
178
|
predicate_builder = predicate_builder(table)
|
192
179
|
scope_chain_items = join_scopes(table, predicate_builder)
|
193
180
|
klass_scope = klass_join_scope(table, predicate_builder)
|
194
181
|
|
182
|
+
key = join_keys.key
|
183
|
+
foreign_key = join_keys.foreign_key
|
184
|
+
|
185
|
+
klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
|
186
|
+
|
195
187
|
if type
|
196
188
|
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
197
189
|
end
|
198
190
|
|
191
|
+
if klass.finder_needs_type_condition?
|
192
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
193
|
+
end
|
194
|
+
|
199
195
|
scope_chain_items.inject(klass_scope, &:merge!)
|
200
196
|
end
|
201
197
|
|
@@ -133,11 +133,12 @@ module ActiveRecord
|
|
133
133
|
relation = apply_join_dependency
|
134
134
|
|
135
135
|
if operation.to_s.downcase == "count"
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
relation.order_values = []
|
136
|
+
unless distinct_value || distinct_select?(column_name || select_for_count)
|
137
|
+
relation.distinct!
|
138
|
+
relation.select_values = [ klass.primary_key || table[Arel.star] ]
|
140
139
|
end
|
140
|
+
# PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
|
141
|
+
relation.order_values = []
|
141
142
|
end
|
142
143
|
|
143
144
|
relation.calculate(operation, column_name)
|
@@ -190,11 +191,9 @@ module ActiveRecord
|
|
190
191
|
relation = apply_join_dependency
|
191
192
|
relation.pluck(*column_names)
|
192
193
|
else
|
193
|
-
enforce_raw_sql_whitelist(column_names)
|
194
|
+
klass.enforce_raw_sql_whitelist(column_names)
|
194
195
|
relation = spawn
|
195
|
-
relation.select_values = column_names
|
196
|
-
@klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
|
197
|
-
}
|
196
|
+
relation.select_values = column_names
|
198
197
|
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
199
198
|
result.cast_values(klass.attribute_types)
|
200
199
|
end
|
@@ -209,7 +208,6 @@ module ActiveRecord
|
|
209
208
|
end
|
210
209
|
|
211
210
|
private
|
212
|
-
|
213
211
|
def has_include?(column_name)
|
214
212
|
eager_loading? || (includes_values.present? && column_name && column_name != :all)
|
215
213
|
end
|
@@ -224,10 +222,12 @@ module ActiveRecord
|
|
224
222
|
if operation == "count"
|
225
223
|
column_name ||= select_for_count
|
226
224
|
if column_name == :all
|
227
|
-
if distinct
|
225
|
+
if !distinct
|
226
|
+
distinct = distinct_select?(select_for_count) if group_values.empty?
|
227
|
+
elsif group_values.any? || select_values.empty? && order_values.empty?
|
228
228
|
column_name = primary_key
|
229
229
|
end
|
230
|
-
elsif column_name
|
230
|
+
elsif distinct_select?(column_name)
|
231
231
|
distinct = nil
|
232
232
|
end
|
233
233
|
end
|
@@ -239,6 +239,10 @@ module ActiveRecord
|
|
239
239
|
end
|
240
240
|
end
|
241
241
|
|
242
|
+
def distinct_select?(column_name)
|
243
|
+
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
244
|
+
end
|
245
|
+
|
242
246
|
def aggregate_column(column_name)
|
243
247
|
return column_name if Arel::Expressions === column_name
|
244
248
|
|
@@ -383,7 +387,7 @@ module ActiveRecord
|
|
383
387
|
case operation
|
384
388
|
when "count" then value.to_i
|
385
389
|
when "sum" then type.deserialize(value || 0)
|
386
|
-
when "average" then value.respond_to?(:to_d) ? value.to_d : value
|
390
|
+
when "average" then value && value.respond_to?(:to_d) ? value.to_d : value
|
387
391
|
else type.deserialize(value)
|
388
392
|
end
|
389
393
|
end
|
@@ -319,7 +319,7 @@ module ActiveRecord
|
|
319
319
|
|
320
320
|
relation = construct_relation_for_exists(conditions)
|
321
321
|
|
322
|
-
skip_query_cache_if_necessary { connection.
|
322
|
+
skip_query_cache_if_necessary { connection.select_one(relation.arel, "#{name} Exists") } ? true : false
|
323
323
|
rescue ::RangeError
|
324
324
|
false
|
325
325
|
end
|
@@ -359,7 +359,11 @@ module ActiveRecord
|
|
359
359
|
end
|
360
360
|
|
361
361
|
def construct_relation_for_exists(conditions)
|
362
|
-
|
362
|
+
if distinct_value && offset_value
|
363
|
+
relation = except(:order).limit!(1)
|
364
|
+
else
|
365
|
+
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
366
|
+
end
|
363
367
|
|
364
368
|
case conditions
|
365
369
|
when Array, Hash
|
@@ -175,9 +175,7 @@ module ActiveRecord
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def merge_clauses
|
178
|
-
|
179
|
-
relation.from_clause = other.from_clause
|
180
|
-
end
|
178
|
+
relation.from_clause = other.from_clause if replace_from_clause?
|
181
179
|
|
182
180
|
where_clause = relation.where_clause.merge(other.where_clause)
|
183
181
|
relation.where_clause = where_clause unless where_clause.empty?
|
@@ -185,6 +183,11 @@ module ActiveRecord
|
|
185
183
|
having_clause = relation.having_clause.merge(other.having_clause)
|
186
184
|
relation.having_clause = having_clause unless having_clause.empty?
|
187
185
|
end
|
186
|
+
|
187
|
+
def replace_from_clause?
|
188
|
+
relation.from_clause.empty? && !other.from_clause.empty? &&
|
189
|
+
relation.klass.base_class == other.klass.base_class
|
190
|
+
end
|
188
191
|
end
|
189
192
|
end
|
190
193
|
end
|
@@ -93,16 +93,21 @@ module ActiveRecord
|
|
93
93
|
queries.reduce(&:or)
|
94
94
|
elsif table.aggregated_with?(key)
|
95
95
|
mapping = table.reflect_on_aggregation(key).mapping
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
96
|
+
values = value.nil? ? [nil] : Array.wrap(value)
|
97
|
+
if mapping.length == 1 || values.empty?
|
98
|
+
column_name, aggr_attr = mapping.first
|
99
|
+
values = values.map do |object|
|
100
|
+
object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
|
101
|
+
end
|
102
|
+
build(table.arel_attribute(column_name), values)
|
103
|
+
else
|
104
|
+
queries = values.map do |object|
|
105
|
+
mapping.map do |field_attr, aggregate_attr|
|
106
|
+
build(table.arel_attribute(field_attr), object.try!(aggregate_attr))
|
107
|
+
end.reduce(&:and)
|
108
|
+
end
|
109
|
+
queries.reduce(&:or)
|
104
110
|
end
|
105
|
-
queries.reduce(&:or)
|
106
111
|
else
|
107
112
|
build(table.arel_attribute(key), value)
|
108
113
|
end
|
@@ -18,13 +18,15 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def nil?
|
21
|
-
|
22
|
-
|
21
|
+
unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
22
|
+
value_before_type_cast.nil? ||
|
23
|
+
type.respond_to?(:subtype, true) && value_for_database.nil?
|
24
|
+
end
|
23
25
|
end
|
24
26
|
|
25
27
|
def boundable?
|
26
28
|
return @_boundable if defined?(@_boundable)
|
27
|
-
|
29
|
+
value_for_database unless value_before_type_cast.is_a?(StatementCache::Substitute)
|
28
30
|
@_boundable = true
|
29
31
|
rescue ::RangeError
|
30
32
|
@_boundable = false
|
@@ -232,9 +232,6 @@ module ActiveRecord
|
|
232
232
|
|
233
233
|
def _select!(*fields) # :nodoc:
|
234
234
|
fields.flatten!
|
235
|
-
fields.map! do |field|
|
236
|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field).to_sym : field
|
237
|
-
end
|
238
235
|
self.select_values += fields
|
239
236
|
self
|
240
237
|
end
|
@@ -1051,11 +1048,14 @@ module ActiveRecord
|
|
1051
1048
|
|
1052
1049
|
def arel_columns(columns)
|
1053
1050
|
columns.flat_map do |field|
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1051
|
+
case field
|
1052
|
+
when Symbol
|
1053
|
+
arel_column(field.to_s) do |attr_name|
|
1054
|
+
connection.quote_table_name(attr_name)
|
1055
|
+
end
|
1056
|
+
when String
|
1057
|
+
arel_column(field, &:itself)
|
1058
|
+
when Proc
|
1059
1059
|
field.call
|
1060
1060
|
else
|
1061
1061
|
field
|
@@ -1063,6 +1063,21 @@ module ActiveRecord
|
|
1063
1063
|
end
|
1064
1064
|
end
|
1065
1065
|
|
1066
|
+
def arel_column(field)
|
1067
|
+
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
|
1068
|
+
from = from_clause.name || from_clause.value
|
1069
|
+
|
1070
|
+
if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
1071
|
+
arel_attribute(field)
|
1072
|
+
else
|
1073
|
+
yield field
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def table_name_matches?(from)
|
1078
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
|
1079
|
+
end
|
1080
|
+
|
1066
1081
|
def reverse_sql_order(order_query)
|
1067
1082
|
if order_query.empty?
|
1068
1083
|
return [arel_attribute(primary_key).desc] if primary_key
|
@@ -1144,14 +1159,14 @@ module ActiveRecord
|
|
1144
1159
|
order_args.map! do |arg|
|
1145
1160
|
case arg
|
1146
1161
|
when Symbol
|
1147
|
-
|
1162
|
+
order_column(arg.to_s).asc
|
1148
1163
|
when Hash
|
1149
1164
|
arg.map { |field, dir|
|
1150
1165
|
case field
|
1151
1166
|
when Arel::Nodes::SqlLiteral
|
1152
1167
|
field.send(dir.downcase)
|
1153
1168
|
else
|
1154
|
-
|
1169
|
+
order_column(field.to_s).send(dir.downcase)
|
1155
1170
|
end
|
1156
1171
|
}
|
1157
1172
|
else
|
@@ -1160,6 +1175,16 @@ module ActiveRecord
|
|
1160
1175
|
end.flatten!
|
1161
1176
|
end
|
1162
1177
|
|
1178
|
+
def order_column(field)
|
1179
|
+
arel_column(field) do |attr_name|
|
1180
|
+
if attr_name == "count" && !group_values.empty?
|
1181
|
+
arel_attribute(attr_name)
|
1182
|
+
else
|
1183
|
+
Arel.sql(connection.quote_table_name(attr_name))
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
1163
1188
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1164
1189
|
# blank-like object were initially passed into the query method, then this
|
1165
1190
|
# method will not raise an error.
|
@@ -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,
|
@@ -87,8 +87,8 @@ module ActiveRecord
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
def self.create(connection,
|
91
|
-
relation = block.call Params.new
|
90
|
+
def self.create(connection, callable = nil, &block)
|
91
|
+
relation = (callable || block).call Params.new
|
92
92
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
93
93
|
bind_map = BindMap.new(binds)
|
94
94
|
new(query_builder, bind_map, relation.klass)
|
@@ -340,6 +340,7 @@ module ActiveRecord
|
|
340
340
|
# Ensure that it is not called if the object was never persisted (failed create),
|
341
341
|
# but call it after the commit of a destroyed object.
|
342
342
|
def committed!(should_run_callbacks: true) #:nodoc:
|
343
|
+
force_clear_transaction_record_state
|
343
344
|
if should_run_callbacks && (destroyed? || persisted?)
|
344
345
|
@_committed_already_called = true
|
345
346
|
_run_commit_without_transaction_enrollment_callbacks
|
@@ -347,7 +348,6 @@ module ActiveRecord
|
|
347
348
|
end
|
348
349
|
ensure
|
349
350
|
@_committed_already_called = false
|
350
|
-
force_clear_transaction_record_state
|
351
351
|
end
|
352
352
|
|
353
353
|
# Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
|