diff --git a/NEWS b/NEWS index 8f4f258c..0cdc3958 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,7 @@ What's new in psycopg 2.4.5 - Discard any result produced by 'executemany()' (ticket #133). - Fixed pickling of FixedOffsetTimezone objects (ticket #135). - Release the GIL around PQgetResult calls after COPY (ticket #140). + - Fixed empty strings handling in composite caster (ticket #141). What's new in psycopg 2.4.5 diff --git a/lib/extras.py b/lib/extras.py index 5b45bdd1..22ae3f6e 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -852,9 +852,9 @@ class CompositeCaster(object): for m in self._re_tokenize.finditer(s): if m is None: raise psycopg2.InterfaceError("can't parse type: %r" % s) - if m.group(1): + if m.group(1) is not None: rv.append(None) - elif m.group(2): + elif m.group(2) is not None: rv.append(self._re_undouble.sub(r"\1", m.group(2))) else: rv.append(m.group(3)) diff --git a/tests/test_types_extras.py b/tests/test_types_extras.py index 559d8c9e..944ab894 100755 --- a/tests/test_types_extras.py +++ b/tests/test_types_extras.py @@ -501,6 +501,7 @@ class AdaptTypeTestCase(unittest.TestCase): self.assertEqual(CompositeCaster.tokenize(s), v) ok("(,)", [None, None]) + ok('(,"")', [None, '']) ok('(hello,,10.234,2010-11-11)', ['hello', None, '10.234', '2010-11-11']) ok('(10,"""")', ['10', '"']) ok('(10,",")', ['10', ',']) @@ -555,6 +556,26 @@ class AdaptTypeTestCase(unittest.TestCase): self.assertEqual(v.astring, "hello") self.assertEqual(v.adate, date(2011,1,2)) + @skip_if_no_composite + def test_empty_string(self): + # issue #141 + self._create_type("type_ss", [('s1', 'text'), ('s2', 'text')]) + curs = self.conn.cursor() + psycopg2.extras.register_composite("type_ss", curs) + + def ok(t): + curs.execute("select %s::type_ss", (t,)) + rv = curs.fetchone()[0] + self.assertEqual(t, rv) + + ok(('a', 'b')) + ok(('a', '')) + ok(('', 'b')) + ok(('a', None)) + ok((None, 'b')) + ok(('', '')) + ok((None, None)) + @skip_if_no_composite def test_cast_nested(self): self._create_type("type_is",