aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorSean Griffin <sean@seantheprogrammer.com>2016-08-31 12:06:30 -0400
committerSean Griffin <sean@seantheprogrammer.com>2016-08-31 12:06:30 -0400
commit7ba3a48df5bfdc5e98506bb829f937e03b55a5b3 (patch)
tree653b354c46f2978c1debd79091eb769219ea8ebd /activerecord
parent03d3f036a71d433c661d167596989ae6896e911c (diff)
downloadrails-7ba3a48df5bfdc5e98506bb829f937e03b55a5b3.tar.gz
rails-7ba3a48df5bfdc5e98506bb829f937e03b55a5b3.tar.bz2
rails-7ba3a48df5bfdc5e98506bb829f937e03b55a5b3.zip
Attempt to maintain encoding for arrays of strings with PG
I still think that this is something that should be handled in the pg gem, but it's not going to end up happening there so we'll do it here instead. Once we bump to pg 0.19 we can pass the encoding to the `encode` method instead. This issue occurs because C has no concept of encoding (or strings, really). The bytes that we pass here when sending the value to the database will always be interpreted as whatever encoding the connection is currently configured to use. That means that roundtripping to the database will lose no information However, after assigning we round trip through our type system without hitting the database. The only way that we can do the "correct" thin here would be to actually give a reference to the connection to the array type and have it check the current value of the connection's encoding -- which I'm strongly opposed to. We could also pass in the encoding when it's constructed, but since that can change independently of the type I'm not a huge fan of that either. This feels like a reasonable middle ground, where if we have an array of strings we simply use the encoding of the string we're given. Fixes #26326.
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb13
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb6
3 files changed, 25 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f62d29b5bf..3eac34d65e 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,10 @@
+* PostgreSQL array columns will now respect the encoding of strings contained
+ in the array.
+
+ Fixes #26326.
+
+ *Sean Griffin*
+
* Inverse association instances will now be set before `after_find` or
`after_initialize` callbacks are run.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 1a66afb23a..b969503178 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -33,7 +33,11 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Array)
- @pg_encoder.encode(type_cast_array(value, :serialize))
+ result = @pg_encoder.encode(type_cast_array(value, :serialize))
+ if encoding = determine_encoding_of_strings(value)
+ result.encode!(encoding)
+ end
+ result
else
super
end
@@ -63,6 +67,13 @@ module ActiveRecord
@subtype.public_send(method, value)
end
end
+
+ def determine_encoding_of_strings(value)
+ case value
+ when ::Array then determine_encoding_of_strings(value.first)
+ when ::String then value.encoding
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 60da9d8859..97960b6c51 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -311,6 +311,12 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert_equal ["has already been taken"], e2.errors[:tags], "Should have uniqueness message for tags"
end
+ def test_encoding_arrays_of_utf8_strings
+ string_with_utf8 = "novĂ˝"
+ assert_equal [string_with_utf8], @type.deserialize(@type.serialize([string_with_utf8]))
+ assert_equal [[string_with_utf8]], @type.deserialize(@type.serialize([[string_with_utf8]]))
+ end
+
private
def assert_cycle(field, array)
# test creation