From 08b7cfc86278765a44ffa784df52af35c6d9e7c5 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Wed, 4 Aug 2021 14:28:51 +0100 Subject: [PATCH] Fix description for cursors returning no column --- psycopg/psycopg/cursor.py | 13 +++++++++++-- tests/test_cursor.py | 16 ++++++++++++++++ tests/test_server_cursor.py | 7 +++++++ tests/test_server_cursor_async.py | 9 +++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/psycopg/psycopg/cursor.py b/psycopg/psycopg/cursor.py index df48cd3ac..7ecd894fa 100644 --- a/psycopg/psycopg/cursor.py +++ b/psycopg/psycopg/cursor.py @@ -123,9 +123,18 @@ class BaseCursor(Generic[ConnectionType, Row]): `!None` if the current resultset didn't return tuples. """ res = self.pgresult - if not (res and res.nfields): + + # We return columns if we have nfields, but also if we don't but + # the query said we got tuples (mostly to handle the super useful + # query "SELECT ;" + if res and ( + res.nfields + or res.status == ExecStatus.TUPLES_OK + or res.status == ExecStatus.SINGLE_TUPLE + ): + return [Column(self, i) for i in range(res.nfields)] + else: return None - return [Column(self, i) for i in range(res.nfields)] @property def rowcount(self) -> int: diff --git a/tests/test_cursor.py b/tests/test_cursor.py index c11178987..597e56a42 100644 --- a/tests/test_cursor.py +++ b/tests/test_cursor.py @@ -290,6 +290,11 @@ def test_iter_stop(conn): def test_row_factory(conn): cur = conn.cursor(row_factory=my_row_factory) + + cur.execute("reset search_path") + with pytest.raises(psycopg.ProgrammingError): + cur.fetchone() + cur.execute("select 'foo' as bar") (r,) = cur.fetchone() assert r == "FOObar" @@ -442,6 +447,12 @@ def test_stream_row_factory(conn): assert next(it).a == 2 +def test_stream_no_col(conn): + cur = conn.cursor() + it = iter(cur.stream("select")) + assert list(it) == [()] + + @pytest.mark.parametrize( "query", [ @@ -548,6 +559,11 @@ class TestColumn: unpickled = pickle.loads(pickled) assert [tuple(d) for d in description] == [tuple(d) for d in unpickled] + def test_no_col_query(self, conn): + cur = conn.execute("select") + assert cur.description == [] + assert cur.fetchall() == [()] + def test_str(conn): cur = conn.cursor() diff --git a/tests/test_server_cursor.py b/tests/test_server_cursor.py index ecd61e9e8..dfd13b19a 100644 --- a/tests/test_server_cursor.py +++ b/tests/test_server_cursor.py @@ -189,6 +189,13 @@ def test_nextset(conn): assert not cur.nextset() +def test_no_result(conn): + with conn.cursor("foo") as cur: + cur.execute("select generate_series(1, %s) as bar where false", (3,)) + assert len(cur.description) == 1 + assert cur.fetchall() == [] + + def test_row_factory(conn): n = 0 diff --git a/tests/test_server_cursor_async.py b/tests/test_server_cursor_async.py index af85b68e9..4b724af32 100644 --- a/tests/test_server_cursor_async.py +++ b/tests/test_server_cursor_async.py @@ -195,6 +195,15 @@ async def test_nextset(aconn): assert not cur.nextset() +async def test_no_result(aconn): + async with aconn.cursor("foo") as cur: + await cur.execute( + "select generate_series(1, %s) as bar where false", (3,) + ) + assert len(cur.description) == 1 + assert (await cur.fetchall()) == [] + + async def test_row_factory(aconn): n = 0 -- 2.47.3