Skip to content

Commit 68fe6b0

Browse files
committed
Properly cast input in update_all
The documentation claims that given values go through "normal AR type casting and serialization", which to me implies `serialize(cast(value))`, not just serialization. The docs were changed to use this wording in #22492. The tests I cited in that PR (which is the same test modified in this commit), is worded in a way that implies it should be using `cast` as well. It's possible that I originally meant "normal type casting" to imply just the call to `serialize`, but given that `update_all(archived: params['archived'])` seems to be pretty common, I'm inclined to make this change as long as no tests are broken from it.
1 parent f49d594 commit 68fe6b0

File tree

3 files changed

+14
-2
lines changed

3 files changed

+14
-2
lines changed

activerecord/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
* `update_all` will now pass its values to `Type#cast` before passing them to
2+
`Type#serialize`. This means that `update_all(foo: 'true')` will properly
3+
persist a boolean.
4+
5+
*Sean Griffin*
6+
17
* Add new error class `StatementTimeout` which will be raised
28
when statement timeout exceeded.
39

activerecord/lib/active_record/sanitization.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ def expand_hash_conditions_for_aggregates(attrs) # :doc:
110110
def sanitize_sql_hash_for_assignment(attrs, table) # :doc:
111111
c = connection
112112
attrs.map do |attr, value|
113-
value = type_for_attribute(attr.to_s).serialize(value)
113+
type = type_for_attribute(attr.to_s)
114+
value = type.serialize(type.cast(value))
114115
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
115116
end.join(", ")
116117
end

activerecord/test/cases/relation_test.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,13 +288,18 @@ def type
288288
:string
289289
end
290290

291+
def cast(value)
292+
raise value unless value == "value from user"
293+
"cast value"
294+
end
295+
291296
def deserialize(value)
292297
raise value unless value == "type cast for database"
293298
"type cast from database"
294299
end
295300

296301
def serialize(value)
297-
raise value unless value == "value from user"
302+
raise value unless value == "cast value"
298303
"type cast for database"
299304
end
300305
end

0 commit comments

Comments
 (0)