queries icon indicating copy to clipboard operation
queries copied to clipboard

Handle resultless statements when rowcount > 0

Open andrewrabert opened this issue 6 years ago • 2 comments

When, for example, an insert without return is executed, attempting to access the queries.Result object's rows raises a psycopg2.ProgrammingError with a message of 'no results to fetch'. The existing check in queries is based on cursor.rowcount and thus fails in this example as rowcount contains the number of rows affected - not necessarily returned.

This commit adds handling of said exception and matches the behaviors if the rowcount were 0.

andrewrabert avatar May 22 '19 03:05 andrewrabert

Can you check if there is a corresponding constant for the exception in psycopg2.errorcodes instead of doing string comparison? Ideally it'd be something like:

except psycopg2.ProgrammingError as err:
    if err.pgcode == psycopg2.errorcodes.NO_DATA:

I'm not sure what the error code # is, but here's where they are enumerated:

https://github.com/psycopg/psycopg2/blob/master/lib/errorcodes.py

gmr avatar May 22 '19 17:05 gmr

Both pgcode and pgerror are None.

This example replicates the issue in the queries dev env without any mocking. In addition to printing exception values, I also printed cur values for both the successful and failed queries. The only noticeable difference between the two is that the successful query's description attribute is set while the failed is None.

Code

import os
import psycopg2

conn = psycopg2.connect(
    host=os.environ['PGHOST'],
    port=os.environ['PGPORT'],
    user='postgres'
)


# create table
with conn.cursor() as cur:
    cur.execute('DROP TABLE IF EXISTS example; CREATE TABLE example (id INTEGER)')
    conn.commit()

# insert
with conn.cursor() as cur:
    cur.execute('INSERT INTO example (id) VALUES (0) RETURNING *')
    for attr in dir(cur):
        if not attr.startswith('__'):
            print(attr, getattr(cur, attr))
    print()
    conn.commit()
    cur.fetchall()

print()

# insert no return
with conn.cursor() as cur:
    cur.execute('INSERT INTO example (id) VALUES (0)')
    for attr in dir(cur):
        if not attr.startswith('__'):
            print(attr, getattr(cur, attr))
    print()

    conn.commit()
    try:
        cur.fetchall()
    except psycopg2.ProgrammingError as e:
        print()
        for attr in dir(e):
            if not attr.startswith('__'):
                print(attr, getattr(e, attr))
        print()
        raise

Output

arraysize 1
binary_types None
callproc <built-in method callproc of psycopg2.extensions.cursor object at 0x7fec53722428>
cast <built-in method cast of psycopg2.extensions.cursor object at 0x7fec53722428>
close <built-in method close of psycopg2.extensions.cursor object at 0x7fec53722428>
closed False
connection <connection object at 0x7fec56312c28; dsn: 'host=127.0.0.1 port=32768 user=postgres', closed: 0>
copy_expert <built-in method copy_expert of psycopg2.extensions.cursor object at 0x7fec53722428>
copy_from <built-in method copy_from of psycopg2.extensions.cursor object at 0x7fec53722428>
copy_to <built-in method copy_to of psycopg2.extensions.cursor object at 0x7fec53722428>
description (Column(name='id', type_code=23, display_size=None, internal_size=4, precision=None, scale=None, null_ok=None),)
execute <built-in method execute of psycopg2.extensions.cursor object at 0x7fec53722428>
executemany <built-in method executemany of psycopg2.extensions.cursor object at 0x7fec53722428>
fetchall <built-in method fetchall of psycopg2.extensions.cursor object at 0x7fec53722428>
fetchmany <built-in method fetchmany of psycopg2.extensions.cursor object at 0x7fec53722428>
fetchone <built-in method fetchone of psycopg2.extensions.cursor object at 0x7fec53722428>
itersize 2000
lastrowid 0
mogrify <built-in method mogrify of psycopg2.extensions.cursor object at 0x7fec53722428>
name None
nextset <built-in method nextset of psycopg2.extensions.cursor object at 0x7fec53722428>
query b'INSERT INTO example (id) VALUES (0) RETURNING *'
row_factory None
rowcount 1
rownumber 0
scroll <built-in method scroll of psycopg2.extensions.cursor object at 0x7fec53722428>
scrollable None
setinputsizes <built-in method setinputsizes of psycopg2.extensions.cursor object at 0x7fec53722428>
setoutputsize <built-in method setoutputsize of psycopg2.extensions.cursor object at 0x7fec53722428>
statusmessage INSERT 0 1
string_types None
typecaster None
tzinfo_factory <class 'psycopg2.tz.FixedOffsetTimezone'>
withhold False


arraysize 1
binary_types None
callproc <built-in method callproc of psycopg2.extensions.cursor object at 0x7fec53722520>
cast <built-in method cast of psycopg2.extensions.cursor object at 0x7fec53722520>
close <built-in method close of psycopg2.extensions.cursor object at 0x7fec53722520>
closed False
connection <connection object at 0x7fec56312c28; dsn: 'host=127.0.0.1 port=32768 user=postgres', closed: 0>
copy_expert <built-in method copy_expert of psycopg2.extensions.cursor object at 0x7fec53722520>
copy_from <built-in method copy_from of psycopg2.extensions.cursor object at 0x7fec53722520>
copy_to <built-in method copy_to of psycopg2.extensions.cursor object at 0x7fec53722520>
description None
execute <built-in method execute of psycopg2.extensions.cursor object at 0x7fec53722520>
executemany <built-in method executemany of psycopg2.extensions.cursor object at 0x7fec53722520>
fetchall <built-in method fetchall of psycopg2.extensions.cursor object at 0x7fec53722520>
fetchmany <built-in method fetchmany of psycopg2.extensions.cursor object at 0x7fec53722520>
fetchone <built-in method fetchone of psycopg2.extensions.cursor object at 0x7fec53722520>
itersize 2000
lastrowid 0
mogrify <built-in method mogrify of psycopg2.extensions.cursor object at 0x7fec53722520>
name None
nextset <built-in method nextset of psycopg2.extensions.cursor object at 0x7fec53722520>
query b'INSERT INTO example (id) VALUES (0)'
row_factory None
rowcount 1
rownumber 0
scroll <built-in method scroll of psycopg2.extensions.cursor object at 0x7fec53722520>
scrollable None
setinputsizes <built-in method setinputsizes of psycopg2.extensions.cursor object at 0x7fec53722520>
setoutputsize <built-in method setoutputsize of psycopg2.extensions.cursor object at 0x7fec53722520>
statusmessage INSERT 0 1
string_types None
typecaster None
tzinfo_factory <class 'psycopg2.tz.FixedOffsetTimezone'>
withhold False


args ('no results to fetch',)
cursor None
diag <psycopg2.extensions.Diagnostics object at 0x7fec5635c600>
pgcode None
pgerror None
with_traceback <built-in method with_traceback of ProgrammingError object at 0x7fec562cc268>

Traceback (most recent call last):
  File "t.py", line 38, in <module>
    cur.fetchall()
psycopg2.ProgrammingError: no results to fetch

andrewrabert avatar Jun 17 '19 15:06 andrewrabert