blob: fb4e0de2a8c5ec62ca9011e01eb5a5b187bb903d (
plain) (
tree)
|
|
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
class Array < Type::Value # :nodoc:
include Type::Helpers::Mutable
# Loads pg_array_parser if available. String parsing can be
# performed quicker by a native extension, which will not create
# a large amount of Ruby objects that will need to be garbage
# collected. pg_array_parser has a C and Java extension
begin
require 'pg_array_parser'
include PgArrayParser
rescue LoadError
require 'active_record/connection_adapters/postgresql/array_parser'
include PostgreSQL::ArrayParser
end
attr_reader :subtype, :delimiter
delegate :type, :user_input_in_time_zone, to: :subtype
def initialize(subtype, delimiter = ',')
@subtype = subtype
@delimiter = delimiter
end
def deserialize(value)
if value.is_a?(::String)
type_cast_array(parse_pg_array(value), :deserialize)
else
super
end
end
def cast(value)
if value.is_a?(::String)
value = parse_pg_array(value)
end
type_cast_array(value, :cast)
end
def serialize(value)
if value.is_a?(::Array)
cast_value_for_database(value)
else
super
end
end
def ==(other)
other.is_a?(Array) &&
subtype == other.subtype &&
delimiter == other.delimiter
end
private
def type_cast_array(value, method)
if value.is_a?(::Array)
value.map { |item| type_cast_array(item, method) }
else
@subtype.public_send(method, value)
end
end
def cast_value_for_database(value)
if value.is_a?(::Array)
casted_values = value.map { |item| cast_value_for_database(item) }
"{#{casted_values.join(delimiter)}}"
else
quote_and_escape(subtype.serialize(value))
end
end
ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
def quote_and_escape(value)
case value
when ::String
if string_requires_quoting?(value)
value = value.gsub(/\\/, ARRAY_ESCAPE)
value.gsub!(/"/,"\\\"")
%("#{value}")
else
value
end
when nil then "NULL"
else value
end
end
# See http://www.postgresql.org/docs/9.2/static/arrays.html#ARRAYS-IO
# for a list of all cases in which strings will be quoted.
def string_requires_quoting?(string)
string.empty? ||
string == "NULL" ||
string =~ /[\{\}"\\\s]/ ||
string.include?(delimiter)
end
end
end
end
end
end
|