提交 e2763b48 编写于 作者: S Sean Griffin

Use the type object for type casting HStore columns

上级 03c9c0e2
......@@ -2,54 +2,11 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module Cast # :nodoc:
def hstore_to_string(object) # :nodoc:
if Hash === object
string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ')
string
else
object
end
end
def string_to_hstore(string) # :nodoc:
if string.nil?
nil
elsif String === string
Hash[string.scan(HstorePair).map { |k, v|
v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
[k, v]
}]
else
string
end
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
"[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
end
private
HstorePair = begin
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
/(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
end
def escape_hstore(value)
if value.nil?
'NULL'
else
if value == ""
'""'
else
'"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
end
end
end
end
end
end
......
......@@ -10,16 +10,48 @@ def type
end
def type_cast_from_database(value)
ConnectionAdapters::PostgreSQLColumn.string_to_hstore(value)
if value.is_a?(::String)
::Hash[value.scan(HstorePair).map { |k, v|
v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
[k, v]
}]
else
value
end
end
def type_cast_for_database(value)
ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
if value.is_a?(::Hash)
value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(', ')
else
value
end
end
def accessor
ActiveRecord::Store::StringKeyedHashAccessor
end
private
HstorePair = begin
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
/(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
end
def escape_hstore(value)
if value.nil?
'NULL'
else
if value == ""
'""'
else
'"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
end
end
end
end
end
end
......
......@@ -28,11 +28,6 @@ def quote(value, column = nil) #:nodoc:
else
super
end
when Hash
case sql_type
when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
else super
end
when Float
if value.infinite? && column.type == :datetime
"'#{value.to_s.downcase}'"
......@@ -80,11 +75,6 @@ def type_cast(value, column)
else
super
end
when Hash
case column.sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
else super
end
else
super
end
......
......@@ -112,13 +112,7 @@ def test_cast_value_on_write
end
def test_type_cast_hstore
assert @column
data = "\"1\"=>\"2\""
hash = @column.class.string_to_hstore data
assert_equal({'1' => '2'}, hash)
assert_equal({'1' => '2'}, @column.type_cast_from_database(data))
assert_equal({'1' => '2'}, @column.type_cast_from_database("\"1\"=>\"2\""))
assert_equal({}, @column.type_cast_from_database(""))
assert_equal({'key'=>nil}, @column.type_cast_from_database('key => NULL'))
assert_equal({'c'=>'}','"a"'=>'b "a b'}, @column.type_cast_from_database(%q(c=>"}", "\"a\""=>"b \"a b")))
......@@ -173,19 +167,19 @@ def test_changes_in_place
end
def test_gen1
assert_equal(%q(" "=>""), @column.class.hstore_to_string({' '=>''}))
assert_equal(%q(" "=>""), @column.cast_type.type_cast_for_database({' '=>''}))
end
def test_gen2
assert_equal(%q(","=>""), @column.class.hstore_to_string({','=>''}))
assert_equal(%q(","=>""), @column.cast_type.type_cast_for_database({','=>''}))
end
def test_gen3
assert_equal(%q("="=>""), @column.class.hstore_to_string({'='=>''}))
assert_equal(%q("="=>""), @column.cast_type.type_cast_for_database({'='=>''}))
end
def test_gen4
assert_equal(%q(">"=>""), @column.class.hstore_to_string({'>'=>''}))
assert_equal(%q(">"=>""), @column.cast_type.type_cast_for_database({'>'=>''}))
end
def test_parse1
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册