Permalink
Browse files

Merge pull request #15778 from sgrif/sg-pg-mutable-arrays

Move pg array database type casting to the Array type
  • Loading branch information...
2 parents 0724706 + d5d7349 commit 838c3d25e7ee904d7a9b084c5900491211eda0e9 @matthewd matthewd committed Jun 17, 2014
@@ -38,21 +38,6 @@ def json_to_string(object) # :nodoc:
end
end
- def array_to_string(value, column, adapter) # :nodoc:
- casted_values = value.map do |val|
- if String === val
- if val == "NULL"
- "\"#{val}\""
- else
- quote_and_escape(adapter.type_cast(val, column, true))
- end
- else
- adapter.type_cast(val, column, true)
- end
- end
- "{#{casted_values.join(',')}}"
- end
-
def range_to_string(object) # :nodoc:
from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
@@ -86,19 +71,6 @@ def escape_hstore(value)
end
end
end
-
- ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
-
- def quote_and_escape(value)
- case value
- when "NULL", Numeric
- value
- else
- value = value.gsub(/\\/, ARRAY_ESCAPE)
- value.gsub!(/"/,"\\\"")
- "\"#{value}\""
- end
- end
end
end
end
@@ -22,6 +22,14 @@ def type_cast_from_user(value)
type_cast_array(value, :type_cast_from_user)
end
+ def type_cast_for_database(value)
+ if value.is_a?(::Array)
+ cast_value_for_database(value)
+ else
+ super
+ end
+ end
+
# 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
@@ -43,6 +51,28 @@ def type_cast_array(value, method)
@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(',')}}"
+ else
+ quote_and_escape(subtype.type_cast_for_database(value))
+ end
+ end
+
+ ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
+
+ def quote_and_escape(value)
+ case value
+ when ::String
+ value = value.gsub(/\\/, ARRAY_ESCAPE)
+ value.gsub!(/"/,"\\\"")
+ %("#{value}")
+ when nil then "NULL"
+ else value
+ end
+ end
end
end
end
@@ -32,11 +32,7 @@ def quote(value, column = nil) #:nodoc:
when 'point' then super(PostgreSQLColumn.point_to_string(value))
when 'json' then super(PostgreSQLColumn.json_to_string(value))
else
- if column.array
- "'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
- else
- super
- end
+ super(value, array_column(column))
end
when Hash
case sql_type
@@ -98,11 +94,7 @@ def type_cast(value, column, array_member = false)
when 'point' then PostgreSQLColumn.point_to_string(value)
when 'json' then PostgreSQLColumn.json_to_string(value)
else
- if column.array
- PostgreSQLColumn.array_to_string(value, column, self)
- else
- super(value, column)
- end
+ super(value, array_column(column))
end
when Hash
case column.sql_type
@@ -185,6 +177,26 @@ def _type_cast(value)
super
end
end
+
+ def array_column(column)
+ if column.array && !column.respond_to?(:type_cast_for_database)
+ OID::Array.new(AdapterProxyType.new(column, self))
+ else
+ column
+ end
+ end
+
+ class AdapterProxyType < SimpleDelegator
+ def initialize(column, adapter)
+ @column = column
+ @adapter = adapter
+ super(column)
+ end
+
+ def type_cast_for_database(value)
+ @adapter.type_cast(value, @column)
+ end
+ end
end
end
end
@@ -7,6 +7,16 @@ def type
:datetime
end
+ def type_cast_for_database(value)
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
+
+ if value.acts_like?(:time)
+ value.send(zone_conversion_method)
+ else
+ super
+ end
+ end
+
private
def cast_value(string)

0 comments on commit 838c3d2

Please sign in to comment.