activerecord 5.2.2 → 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.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -0
  3. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  4. data/lib/active_record/associations/collection_association.rb +4 -5
  5. data/lib/active_record/associations/collection_proxy.rb +8 -34
  6. data/lib/active_record/associations/has_many_association.rb +1 -0
  7. data/lib/active_record/associations/has_many_through_association.rb +6 -11
  8. data/lib/active_record/associations/join_dependency/join_association.rb +28 -7
  9. data/lib/active_record/associations/preloader.rb +1 -1
  10. data/lib/active_record/autosave_association.rb +20 -6
  11. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +33 -10
  12. data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
  13. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -3
  14. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -3
  15. data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
  16. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -8
  17. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
  18. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  19. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  20. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -24
  21. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -0
  22. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -3
  23. data/lib/active_record/core.rb +2 -1
  24. data/lib/active_record/errors.rb +18 -12
  25. data/lib/active_record/gem_version.rb +1 -1
  26. data/lib/active_record/migration/compatibility.rb +15 -15
  27. data/lib/active_record/persistence.rb +3 -1
  28. data/lib/active_record/querying.rb +1 -2
  29. data/lib/active_record/reflection.rb +10 -14
  30. data/lib/active_record/relation/calculations.rb +16 -12
  31. data/lib/active_record/relation/finder_methods.rb +6 -2
  32. data/lib/active_record/relation/merger.rb +6 -3
  33. data/lib/active_record/relation/predicate_builder.rb +14 -9
  34. data/lib/active_record/relation/query_attribute.rb +5 -3
  35. data/lib/active_record/relation/query_methods.rb +35 -10
  36. data/lib/active_record/scoping/default.rb +2 -2
  37. data/lib/active_record/statement_cache.rb +2 -2
  38. data/lib/active_record/transactions.rb +1 -1
  39. metadata +9 -10
@@ -9,7 +9,7 @@ module ActiveRecord
9
9
  module VERSION
10
10
  MAJOR = 5
11
11
  MINOR = 2
12
- TINY = 2
12
+ TINY = 5
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -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
- clear_cache!
22
- sql = connection.send(:change_column_sql, table_name, column_name, type, options)
23
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
24
- change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
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
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column)
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
- cached_columns_hash = connection.schema_cache.columns_hash(table_name)
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 build_join_constraint(table, foreign_table)
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
- relation.distinct!
137
- # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
138
- if (column_name == :all || column_name.nil?) && select_values.empty?
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.map { |cn|
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 && (group_values.any? || select_values.empty? && order_values.empty?)
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 =~ /\s*DISTINCT[\s(]+/i
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.select_value(relation.arel, "#{name} Exists") } ? true : false
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
- relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
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
- if relation.from_clause.empty? && !other.from_clause.empty?
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
- queries = Array.wrap(value).map do |object|
97
- mapping.map do |field_attr, aggregate_attr|
98
- if mapping.size == 1 && !object.respond_to?(aggregate_attr)
99
- build(table.arel_attribute(field_attr), object)
100
- else
101
- build(table.arel_attribute(field_attr), object.send(aggregate_attr))
102
- end
103
- end.reduce(&:and)
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
- !value_before_type_cast.is_a?(StatementCache::Substitute) &&
22
- (value_before_type_cast.nil? || value_for_database.nil?)
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
- nil?
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
- if (Symbol === field || String === field) && (klass.has_attribute?(field) || klass.attribute_alias?(field)) && !from_clause.value
1055
- arel_attribute(field)
1056
- elsif Symbol === field
1057
- connection.quote_table_name(field.to_s)
1058
- elsif Proc === field
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
- arel_attribute(arg).asc
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
- arel_attribute(field).send(dir.downcase)
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 = Proc.new if block_given?
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, block = Proc.new)
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
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: 5.2.2
4
+ version: 5.2.5
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: 2018-12-04 00:00:00.000000000 Z
11
+ date: 2021-03-26 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: 5.2.2
19
+ version: 5.2.5
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: 5.2.2
26
+ version: 5.2.5
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: 5.2.2
33
+ version: 5.2.5
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: 5.2.2
40
+ version: 5.2.5
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -307,8 +307,8 @@ homepage: http://rubyonrails.org
307
307
  licenses:
308
308
  - MIT
309
309
  metadata:
310
- source_code_uri: https://github.com/rails/rails/tree/v5.2.2/activerecord
311
- changelog_uri: https://github.com/rails/rails/blob/v5.2.2/activerecord/CHANGELOG.md
310
+ source_code_uri: https://github.com/rails/rails/tree/v5.2.5/activerecord
311
+ changelog_uri: https://github.com/rails/rails/blob/v5.2.5/activerecord/CHANGELOG.md
312
312
  post_install_message:
313
313
  rdoc_options:
314
314
  - "--main"
@@ -326,8 +326,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
326
326
  - !ruby/object:Gem::Version
327
327
  version: '0'
328
328
  requirements: []
329
- rubyforge_project:
330
- rubygems_version: 2.7.6
329
+ rubygems_version: 3.1.2
331
330
  signing_key:
332
331
  specification_version: 4
333
332
  summary: Object-relational mapper framework (part of Rails).