aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorSean Griffin <sean@seantheprogrammer.com>2017-01-03 11:15:16 -0500
committerSean Griffin <sean@seantheprogrammer.com>2017-01-03 11:15:16 -0500
commit0f1d0b1b5254e3678abaabbebb3362a100c10262 (patch)
tree45524fa9caa4d75e80575c7984979930f9b43edf /activerecord/lib
parent0d8069d36543947dc948cbbd02c8214c352b7aad (diff)
downloadrails-0f1d0b1b5254e3678abaabbebb3362a100c10262.tar.gz
rails-0f1d0b1b5254e3678abaabbebb3362a100c10262.tar.bz2
rails-0f1d0b1b5254e3678abaabbebb3362a100c10262.zip
Consistently apply adapter behavior when serializing arrays
In f1a0fa9 we moved backend specific timestamp behavior out of the type and into the adapter. This was in line with our general attempt to reduce the number of adapter specific type subclasses. However, on PG, the array type performs all serialization, including database encoding in its serialize method. This means that we have converted the value into a string before reaching the database, so no adapter specific logic can be applied (and this also means that timestamp arrays were using the default `.to_s` method on the given object, which likely meant timestamps were being ignored in certain cases as well) Ultimately I want to do a more in depth refactoring which separates database serializer objects from the active model type objects, to give us a less awkward API for introducing the attributes API onto Active Model. However, in the short term, we follow the solution we've applied elsewhere for this. Move behavior off of the type and into the adapter, and use a data object to allow the type to communicate information up the stack. Fixes #27514.
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb29
2 files changed, 41 insertions, 13 deletions
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 8ccdd69b1d..e1a75f8e5e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -5,6 +5,8 @@ module ActiveRecord
class Array < Type::Value # :nodoc:
include Type::Helpers::Mutable
+ Data = Struct.new(:encoder, :values) # :nodoc:
+
attr_reader :subtype, :delimiter
delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype
@@ -17,8 +19,11 @@ module ActiveRecord
end
def deserialize(value)
- if value.is_a?(::String)
+ case value
+ when ::String
type_cast_array(@pg_decoder.decode(value), :deserialize)
+ when Data
+ deserialize(value.values)
else
super
end
@@ -33,11 +38,8 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Array)
- result = @pg_encoder.encode(type_cast_array(value, :serialize))
- if encoding = determine_encoding_of_strings(value)
- result.force_encoding(encoding)
- end
- result
+ casted_values = type_cast_array(value, :serialize)
+ Data.new(@pg_encoder, casted_values)
else
super
end
@@ -58,6 +60,10 @@ module ActiveRecord
value.map(&block)
end
+ def changed_in_place?(raw_old_value, new_value)
+ deserialize(raw_old_value) != new_value
+ end
+
private
def type_cast_array(value, method)
@@ -67,13 +73,6 @@ 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/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index b5031d890f..3783925954 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -92,6 +92,8 @@ module ActiveRecord
else
super
end
+ when OID::Array::Data
+ _quote(encode_array(value))
else
super
end
@@ -106,10 +108,37 @@ module ActiveRecord
{ value: value.to_s, format: 1 }
when OID::Xml::Data, OID::Bit::Data
value.to_s
+ when OID::Array::Data
+ encode_array(value)
else
super
end
end
+
+ def encode_array(array_data)
+ encoder = array_data.encoder
+ values = type_cast_array(array_data.values)
+
+ result = encoder.encode(values)
+ if encoding = determine_encoding_of_strings_in_array(values)
+ result.force_encoding(encoding)
+ end
+ result
+ end
+
+ def determine_encoding_of_strings_in_array(value)
+ case value
+ when ::Array then determine_encoding_of_strings_in_array(value.first)
+ when ::String then value.encoding
+ end
+ end
+
+ def type_cast_array(values)
+ case values
+ when ::Array then values.map { |item| type_cast_array(item) }
+ else _type_cast(values)
+ end
+ end
end
end
end