summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-06-28 11:35:57 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-06-28 11:35:57 -0400
commit04b66bc5e7b8e8e93d78e9c70a533cd39e367aaf (patch)
tree5e014b0285328cc1e8b8db0e669326a23cc6a0d0
parentfc348366f76bdb3072c69ad8e03f305de38d486c (diff)
downloadsqlalchemy-04b66bc5e7b8e8e93d78e9c70a533cd39e367aaf.tar.gz
Fixed bug in HSTORE type where keys/values that contained
backslashed quotes would not be escaped correctly when using the "non native" (i.e. non-psycopg2) means of translating HSTORE data. Patch courtesy Ryan Kelly. [ticket:2766]
-rw-r--r--doc/build/changelog/changelog_08.rst9
-rw-r--r--doc/build/changelog/changelog_09.rst10
-rw-r--r--lib/sqlalchemy/dialects/postgresql/hstore.py6
-rw-r--r--test/dialect/test_postgresql.py37
4 files changed, 59 insertions, 3 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst
index ef87fea45..625b72c0b 100644
--- a/doc/build/changelog/changelog_08.rst
+++ b/doc/build/changelog/changelog_08.rst
@@ -8,6 +8,15 @@
.. change::
:tags: bug, postgresql
+ :tickets: 2766
+
+ Fixed bug in HSTORE type where keys/values that contained
+ backslashed quotes would not be escaped correctly when
+ using the "non native" (i.e. non-psycopg2) means
+ of translating HSTORE data. Patch courtesy Ryan Kelly.
+
+ .. change::
+ :tags: bug, postgresql
:tickets: 2767
Fixed bug where the order of columns in a multi-column
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 57cf0da31..85fd69d8d 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -8,6 +8,16 @@
.. change::
:tags: bug, postgresql
+ :tickets: 2766
+
+ Fixed bug in HSTORE type where keys/values that contained
+ backslashed quotes would not be escaped correctly when
+ using the "non native" (i.e. non-psycopg2) means
+ of translating HSTORE data. Patch courtesy Ryan Kelly.
+ Also in 0.8.2.
+
+ .. change::
+ :tags: bug, postgresql
:tickets: 2767
Fixed bug where the order of columns in a multi-column
diff --git a/lib/sqlalchemy/dialects/postgresql/hstore.py b/lib/sqlalchemy/dialects/postgresql/hstore.py
index d7368ff42..c645e25d2 100644
--- a/lib/sqlalchemy/dialects/postgresql/hstore.py
+++ b/lib/sqlalchemy/dialects/postgresql/hstore.py
@@ -68,11 +68,11 @@ def _parse_hstore(hstore_str):
pair_match = HSTORE_PAIR_RE.match(hstore_str)
while pair_match is not None:
- key = pair_match.group('key')
+ key = pair_match.group('key').replace(r'\"', '"').replace("\\\\", "\\")
if pair_match.group('value_null'):
value = None
else:
- value = pair_match.group('value').replace(r'\"', '"')
+ value = pair_match.group('value').replace(r'\"', '"').replace("\\\\", "\\")
result[key] = value
pos += pair_match.end()
@@ -98,7 +98,7 @@ def _serialize_hstore(val):
if position == 'value' and s is None:
return 'NULL'
elif isinstance(s, util.string_types):
- return '"%s"' % s.replace('"', r'\"')
+ return '"%s"' % s.replace("\\", "\\\\").replace('"', r'\"')
else:
raise ValueError("%r in %s position is not a string." %
(s, position))
diff --git a/test/dialect/test_postgresql.py b/test/dialect/test_postgresql.py
index 46a7b316b..ba42015e8 100644
--- a/test/dialect/test_postgresql.py
+++ b/test/dialect/test_postgresql.py
@@ -2948,6 +2948,16 @@ class HStoreTest(fixtures.TestBase):
'"key1"=>"value1", "key2"=>"value2"'
)
+ def test_bind_serialize_with_slashes_and_quotes(self):
+ from sqlalchemy.engine import default
+
+ dialect = default.DefaultDialect()
+ proc = self.test_table.c.hash.type._cached_bind_processor(dialect)
+ eq_(
+ proc({'\\"a': '\\"1'}),
+ '"\\\\\\"a"=>"\\\\\\"1"'
+ )
+
def test_parse_error(self):
from sqlalchemy.engine import default
@@ -2974,6 +2984,17 @@ class HStoreTest(fixtures.TestBase):
{"key1": "value1", "key2": "value2"}
)
+ def test_result_deserialize_with_slashes_and_quotes(self):
+ from sqlalchemy.engine import default
+
+ dialect = default.DefaultDialect()
+ proc = self.test_table.c.hash.type._cached_result_processor(
+ dialect, None)
+ eq_(
+ proc('"\\\\\\"a"=>"\\\\\\"1"'),
+ {'\\"a': '\\"1'}
+ )
+
def test_bind_serialize_psycopg2(self):
from sqlalchemy.dialects.postgresql import psycopg2
@@ -3288,6 +3309,22 @@ class HStoreRoundTripTest(fixtures.TablesTest):
engine = testing.db
self._test_unicode_round_trip(engine)
+ def test_escaped_quotes_round_trip_python(self):
+ engine = self._non_native_engine()
+ self._test_escaped_quotes_round_trip(engine)
+
+ @testing.only_on("postgresql+psycopg2")
+ def test_escaped_quotes_round_trip_native(self):
+ engine = testing.db
+ self._test_escaped_quotes_round_trip(engine)
+
+ def _test_escaped_quotes_round_trip(self, engine):
+ engine.execute(
+ self.tables.data_table.insert(),
+ {'name': 'r1', 'data': {r'key \"foo\"': r'value \"bar"\ xyz'}}
+ )
+ self._assert_data([{r'key \"foo\"': r'value \"bar"\ xyz'}])
+
class _RangeTypeMixin(object):
__requires__ = 'range_types',
__dialect__ = 'postgresql+psycopg2'