You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Do not rely on dup in forgetting_assignment optimization
Follow-up to rails#46282.
The purpose of the optimization from rails#46282 is to avoid unnecessary
`deserialize` / `cast` / `serialize` calls associated with invoking
`value_for_database` on an attribute that has not changed. `dup`ing the
attribute accomplishes that, but `dup`ing the attribute's value is not
appropriate for some value types. For example, a value of the type
`ActiveModel::Type::ImmutableString` requires `clone` instead of `dup`,
and a value of the type `ActiveRecord::Type::Json` likely requires
`deep_dup`. In some cases the only appropriate method may be
`deserialize(serialize(value))`, such as when a serializer for the type
`ActiveRecord::Type::Serialized` deserializes `ActiveRecord::Base`
instances. (In that case, `dup`ing the value would clear its `id`, and
`clone`ing the value would only produce a shallow copy.) However,
`deserialize(serialize(value))` is expensive and would defeat the
purpose of the optimization.
Instead of `dup`ing the attribute, this commit changes the optimization
to use `with_value_from_database(value_before_type_cast)`, which
parallels `with_value_from_database(value_for_database)` in the base
implementation. This drops the (cast) value entirely, causing a fresh
copy to be deserialized if the attribute is subsequently read. In cases
where the attribute is _not_ subsequently read, this will actually be
more efficient since no extra work is performed. And in cases where the
attribute _is_ subsequently read, it will still be more efficient than
`deserialize(serialize(value))`.
Fixesrails#49809.
0 commit comments