aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb35
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb28
2 files changed, 57 insertions, 6 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 6e169ae5c5..705fc8135d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -52,12 +52,39 @@ module ActiveRecord
def cast_hstore(object)
if Hash === object
- object.map { |k,v| "#{k}=>#{v}" }.join ', '
+ object.map { |k,v|
+ "#{escape_hstore(k)}=>#{escape_hstore(v)}"
+ }.join ', '
else
- kvs = object.split(', ').map { |kv|
- kv.split('=>').map { |k| k[1...-1] }
+ kvs = object.scan(/(?<!\\)".*?(?<!\\)"/).map { |o|
+ unescape_hstore(o[1...-1])
}
- Hash[kvs]
+ Hash[kvs.each_slice(2).to_a]
+ end
+ end
+
+ private
+ def unescape_hstore(value)
+ escape_values = {
+ '\\ ' => ' ',
+ '\\\\' => '\\',
+ '\\"' => '"',
+ '\\=' => '=',
+ }
+ value.gsub(Regexp.union(escape_values.keys)) do |match|
+ escape_values[match]
+ end
+ end
+
+ def escape_hstore(value)
+ escape_values = {
+ ' ' => '\\ ',
+ '\\' => '\\\\',
+ '"' => '\\"',
+ '=' => '\\=',
+ }
+ value.gsub(Regexp.union(escape_values.keys)) do |match|
+ escape_values[match]
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 7267536142..e03c938a25 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -45,9 +45,33 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
end
def test_create
- hash = { 'a' => 'b', '1' => '2' }
+ assert_cycle_hstore('a' => 'b', '1' => '2')
+ end
+
+ def test_quotes
+ assert_cycle_hstore('a' => 'b"ar', '1"foo' => '2')
+ end
+
+ def test_whitespace
+ assert_cycle_hstore('a b' => 'b ar', '1"foo' => '2')
+ end
+
+ def test_backslash
+ assert_cycle_hstore('a\\b' => 'b\\ar', '1"foo' => '2')
+ end
+
+ def test_comma
+ assert_cycle_hstore('a, b' => 'bar', '1"foo' => '2')
+ end
+
+ def test_arrow
+ assert_cycle_hstore('a=>b' => 'bar', '1"foo' => '2')
+ end
+
+ private
+ def assert_cycle_hstore hash
x = Hstore.create!(:tags => hash)
x.reload
- assert_equal hash, x.tags
+ assert_equal(hash, x.tags)
end
end