提交 da3fec2e 编写于 作者: J Josh Goodall 提交者: Genadi Samokovarov

Resolve encoding issues with arrays of hstore (bug 11135).

We didn't have enough encoding for the wire protocol to store an array
of hstore types. So, further encode any hstore that is an array member.
Whilst we're here, ensure it's an HashWithIndifferentAccess being
returned, to be consistent with other serialized forms, and add testing
for arrays of hstore.

So now the following migration:

  enable_extension "hstore"
  create_table :servers do |t|
    t.string  :name
    t.hstore  :interfaces, array: true
  end

produces a model that can used like this, to store an array of hashes:

  server = Server.create(name: "server01", interfaces: [
    { name: "bge0", ipv4: "192.0.2.2", state: "up" },
    { name: "de0", state: "disabled", by: "misha" },
    { name: "fe0", state: "up" },
  ])

More at http://inopinatus.org/2013/07/12/using-arrays-of-hstore-with-rails-4/
上级 3e3ed1ed
* Perform necessary deeper encoding when hstore is inside an array.
Fixes #11135.
*Josh Goodall*, *Genadi Samokovarov*
* Properly detect if a connection is still active before using it
in multi-threaded environments.
......
......@@ -35,11 +35,11 @@ def string_to_bit(value)
end
end
def hstore_to_string(object)
def hstore_to_string(object, array_member = false)
if Hash === object
object.map { |k,v|
"#{escape_hstore(k)}=>#{escape_hstore(v)}"
}.join ','
string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
string = escape_hstore(string) if array_member
string
else
object
end
......@@ -49,10 +49,10 @@ def string_to_hstore(string)
if string.nil?
nil
elsif String === string
Hash[string.scan(HstorePair).map { |k,v|
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]
[k, v]
}]
else
string
......
......@@ -121,7 +121,7 @@ def type_cast(value, column, array_member = false)
end
when Hash
case column.sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
when 'json' then PostgreSQLColumn.json_to_string(value)
else super(value, column)
end
......
......@@ -24,6 +24,7 @@ def setup
@connection.transaction do
@connection.create_table('hstores') do |t|
t.hstore 'tags', :default => ''
t.hstore 'payload', array: true
t.hstore 'settings'
end
end
......@@ -182,6 +183,30 @@ def test_select
assert_equal({'1' => '2'}, x.tags)
end
def test_array_cycle
assert_array_cycle([{"AA" => "BB", "CC" => "DD"}, {"AA" => nil}])
end
def test_array_strings_with_quotes
assert_array_cycle([{'this has' => 'some "s that need to be escaped"'}])
end
def test_array_strings_with_commas
assert_array_cycle([{'this,has' => 'many,values'}])
end
def test_array_strings_with_array_delimiters
assert_array_cycle(['{' => '}'])
end
def test_array_strings_with_null_strings
assert_array_cycle([{'NULL' => 'NULL'}])
end
def test_contains_nils
assert_array_cycle([{'NULL' => nil}])
end
def test_select_multikey
@connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
x = Hstore.first
......@@ -237,6 +262,20 @@ def test_update_all
private
def assert_array_cycle(array)
# test creation
x = Hstore.create!(payload: array)
x.reload
assert_equal(array, x.payload)
# test updating
x = Hstore.create!(payload: [])
x.payload = array
x.save!
x.reload
assert_equal(array, x.payload)
end
def assert_cycle(hash)
# test creation
x = Hstore.create!(:tags => hash)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册