Fixed adaptation of lists of None

Note: lists of lists of None are not supported yet.
This commit is contained in:
Daniele Varrazzo 2015-06-02 12:38:59 +01:00
parent 8d4ac25b56
commit b0058c0cc8
3 changed files with 50 additions and 1 deletions

1
NEWS
View File

@ -15,6 +15,7 @@ New features:
What's new in psycopg 2.6.1 What's new in psycopg 2.6.1
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Lists consisting of only `None` are escaped correctly (:ticket:`#285`).
- Fixed deadlock in multithread programs using OpenSSL (:ticket:`#290`). - Fixed deadlock in multithread programs using OpenSSL (:ticket:`#290`).
- Correctly unlock the connection after error in flush (:ticket:`#294`). - Correctly unlock the connection after error in flush (:ticket:`#294`).
- Fixed ``MinTimeLoggingCursor.callproc()`` (:ticket:`#309`). - Fixed ``MinTimeLoggingCursor.callproc()`` (:ticket:`#309`).

View File

@ -39,6 +39,14 @@ list_quote(listObject *self)
/* adapt the list by calling adapt() recursively and then wrapping /* adapt the list by calling adapt() recursively and then wrapping
everything into "ARRAY[]" */ everything into "ARRAY[]" */
PyObject *tmp = NULL, *str = NULL, *joined = NULL, *res = NULL; PyObject *tmp = NULL, *str = NULL, *joined = NULL, *res = NULL;
/* list consisting of only NULL don't work with the ARRAY[] construct
* so we use the {NULL,...} syntax. Note however that list of lists where
* some element is a list of only null still fails: for that we should use
* the '{...}' syntax uniformly but we cannot do it in the current
* infrastructure. TODO in psycopg3 */
int all_nulls = 1;
Py_ssize_t i, len; Py_ssize_t i, len;
len = PyList_GET_SIZE(self->wrapped); len = PyList_GET_SIZE(self->wrapped);
@ -60,6 +68,7 @@ list_quote(listObject *self)
quoted = microprotocol_getquoted(wrapped, quoted = microprotocol_getquoted(wrapped,
(connectionObject*)self->connection); (connectionObject*)self->connection);
if (quoted == NULL) goto error; if (quoted == NULL) goto error;
all_nulls = 0;
} }
/* here we don't loose a refcnt: SET_ITEM does not change the /* here we don't loose a refcnt: SET_ITEM does not change the
@ -74,7 +83,12 @@ list_quote(listObject *self)
joined = PyObject_CallMethod(str, "join", "(O)", tmp); joined = PyObject_CallMethod(str, "join", "(O)", tmp);
if (joined == NULL) goto error; if (joined == NULL) goto error;
res = Bytes_FromFormat("ARRAY[%s]", Bytes_AsString(joined)); /* PG doesn't like ARRAY[NULL..] */
if (!all_nulls) {
res = Bytes_FromFormat("ARRAY[%s]", Bytes_AsString(joined));
} else {
res = Bytes_FromFormat("'{%s}'", Bytes_AsString(joined));
}
error: error:
Py_XDECREF(tmp); Py_XDECREF(tmp);

View File

@ -192,6 +192,40 @@ class TypesBasicTests(ConnectingTestCase):
self.assertRaises(psycopg2.DataError, self.assertRaises(psycopg2.DataError,
psycopg2.extensions.STRINGARRAY, b(s), curs) psycopg2.extensions.STRINGARRAY, b(s), curs)
def testArrayOfNulls(self):
curs = self.conn.cursor()
curs.execute("""
create table na (
texta text[],
inta int[],
boola boolean[],
textaa text[][],
intaa int[][],
boolaa boolean[][]
)""")
curs.execute("insert into na (texta) values (%s)", ([None],))
curs.execute("insert into na (texta) values (%s)", (['a', None],))
curs.execute("insert into na (texta) values (%s)", ([None, None],))
curs.execute("insert into na (inta) values (%s)", ([None],))
curs.execute("insert into na (inta) values (%s)", ([42, None],))
curs.execute("insert into na (inta) values (%s)", ([None, None],))
curs.execute("insert into na (boola) values (%s)", ([None],))
curs.execute("insert into na (boola) values (%s)", ([True, None],))
curs.execute("insert into na (boola) values (%s)", ([None, None],))
# TODO: array of array of nulls are not supported yet
# curs.execute("insert into na (textaa) values (%s)", ([[None]],))
curs.execute("insert into na (textaa) values (%s)", ([['a', None]],))
# curs.execute("insert into na (textaa) values (%s)", ([[None, None]],))
# curs.execute("insert into na (intaa) values (%s)", ([[None]],))
curs.execute("insert into na (intaa) values (%s)", ([[42, None]],))
# curs.execute("insert into na (intaa) values (%s)", ([[None, None]],))
# curs.execute("insert into na (boolaa) values (%s)", ([[None]],))
curs.execute("insert into na (boolaa) values (%s)", ([[True, None]],))
# curs.execute("insert into na (boolaa) values (%s)", ([[None, None]],))
@testutils.skip_from_python(3) @testutils.skip_from_python(3)
def testTypeRoundtripBuffer(self): def testTypeRoundtripBuffer(self):
o1 = buffer("".join(map(chr, range(256)))) o1 = buffer("".join(map(chr, range(256))))