Handle binary strings in Active Record serialized columns

Serialized attributes stored in BLOB columns will be loaded
with the `ASCII-8BIT` (AKA BINARY) encoding.

So unless the serialized payload is pure ASCII, they need
to have the same internal encoding to be properly compared.

Since the serializer have no way to know how the string will
be stored, it's up to the column type to properly set the
encoding.
This commit is contained in:
Jean Boussier 2020-10-14 11:57:31 +02:00
parent 55badbba3b
commit d6929e5a09
3 changed files with 26 additions and 2 deletions

View File

@ -61,9 +61,12 @@ module ActiveRecord
end
def encoded(value)
unless default_value?(value)
coder.dump(value)
return if default_value?(value)
payload = coder.dump(value)
if payload && binary?
payload.force_encoding(Encoding::BINARY)
end
payload
end
end
end

View File

@ -4,6 +4,7 @@ require "cases/helper"
require "models/person"
require "models/traffic_light"
require "models/post"
require "models/binary_field"
class SerializedAttributeTest < ActiveRecord::TestCase
fixtures :topics, :posts
@ -317,6 +318,20 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal({}, topic.content)
end
if current_adapter?(:Mysql2Adapter)
def test_is_not_changed_when_stored_in_mysql_blob
value = %w(Fée)
model = BinaryField.create!(normal_blob: value, normal_text: value)
model.reload
model.normal_text = value
assert_not_predicate model, :normal_text_changed?
model.normal_blob = value
assert_not_predicate model, :normal_blob_changed?
end
end
def test_values_cast_from_nil_are_persisted_as_nil
# This is required to fulfil the following contract, which must be universally
# true in Active Record:

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class BinaryField < ActiveRecord::Base
serialize :normal_blob
serialize :normal_text
end