diff --git a/NEWS b/NEWS index 788dafb7..ca3ffbb3 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ What's new in psycopg 2.7.6 (:ticket:`#746`). - Fixed building on modern FreeBSD versions with Python 3.7 (:ticket:`#755`). - Fixed hang trying to :sql:`COPY` via `~cursor.execute()` (:ticket:`#781`). +- Fixed adaptation of arrays of empty arrays (:ticket:`#788`). - Fixed segfault accessing the `connection.readonly` and `connection.deferrable` repeatedly (:ticket:`#790`). - `~psycopg2.extras.execute_values()` accepts `~psycopg2.sql.Composable` diff --git a/psycopg/adapter_list.c b/psycopg/adapter_list.c index 3fdff76a..5c6fa693 100644 --- a/psycopg/adapter_list.c +++ b/psycopg/adapter_list.c @@ -55,6 +55,8 @@ list_quote(listObject *self) /* empty arrays are converted to NULLs (still searching for a way to insert an empty array in postgresql */ if (len == 0) { + /* it cannot be ARRAY[] because it would make empty lists unusable + * in any() without a cast. But we may convert it into ARRAY[] below */ res = Bytes_FromString("'{}'"); goto exit; } @@ -79,7 +81,19 @@ list_quote(listObject *self) /* Lists of arrays containing only nulls are also not supported * by the ARRAY construct so we should do some special casing */ - if (!PyList_Check(wrapped) || Bytes_AS_STRING(qs[i])[0] == 'A') { + if (PyList_Check(wrapped)) { + if (Bytes_AS_STRING(qs[i])[0] == 'A') { + all_nulls = 0; + } + else if (0 == strcmp(Bytes_AS_STRING(qs[i]), "'{}'")) { + /* case of issue #788: '{{}}' is not supported but + * array[array[]] is */ + all_nulls = 0; + Py_CLEAR(qs[i]); + qs[i] = Bytes_FromString("ARRAY[]"); + } + } + else { all_nulls = 0; } } diff --git a/tests/test_types_basic.py b/tests/test_types_basic.py index 0f93be22..5afcc9bd 100755 --- a/tests/test_types_basic.py +++ b/tests/test_types_basic.py @@ -166,6 +166,13 @@ class TypesBasicTests(ConnectingTestCase): curs.execute("select col from array_test where id = 2") self.assertEqual(curs.fetchone()[0], []) + @testutils.skip_before_postgres(8, 4) + def testNestedEmptyArray(self): + # issue #788 + curs = self.conn.cursor() + curs.execute("select 10 = any(%s::int[])", ([[]], )) + self.assertFalse(curs.fetchone()[0]) + def testEmptyArrayNoCast(self): s = self.execute("SELECT '{}' AS foo") self.assertEqual(s, '{}')