Skip to content

Recommended way to safely check for "Have we reached the end of all result sets?" #10

@gaconnet

Description

@gaconnet

The docs and implementation seem to be potentially at odds with each other regarding the behavior of nextset() when there are no more result sets.

Docs

Move to the next result set. If there are any result sets left, this returns True. Otherwise, None.

Example

In [3]: cursor = connection.cursor()

In [4]: cursor.execute('SELECT 1')

In [5]: cursor.nextset()

In [6]: cursor.nextset()
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
<ipython-input-6-d5418a8680e7> in <module>()
----> 1 cursor.nextset()

oursqlx/cursor.pyx in oursql.Cursor.nextset (oursqlx/oursql.c:21169)()

oursqlx/cursor.pyx in oursql.Cursor._check_statements (oursqlx/oursql.c:21103)()

ProgrammingError: (None, 'no results available', None)

The docs would lead me to believe that the second call to nextset() should once again have returned None instead of raising an error.

This actually arose from a slightly different context. I'm wanting to build abstractions around result sets, so I'd like to have long-term stable ways to check for each of the following conditions separately:

  1. Have I reached the end of the current result set?
  2. Have I reached the end of all result sets?

Checking for the empty list from fetch*() seems like a stable test for Question 1, but the only way to answer Question 2 seems to be to catch ProgrammingError and look for the string value 'no results available'. That seems less than ideal. I was hoping I could use a nextset() return value of None to answer Question 2, but it too raises ProgrammingError in some scenarios (as demonstrated above).

Here is another simplified example framed within this context.

Example 2

In [13]: cursor = connection.cursor()
In [14]: cursor.execute('SELECT 1')
In [15]: cursor.fetchmany(8)
Out[15]: [(1L,)]
In [16]: cursor.fetchmany(8)
Out[16]: []  # This signals that we've reached the end of *a* result set (i.e. Question 1)

# Now try to start consuming from any potential 2nd result set
In [17]: cursor.fetchmany(8)
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
<ipython-input-17-beb368c5ed13> in <module>()
----> 1 cursor.fetchmany(8)

oursqlx/cursor.pyx in oursql.Cursor.fetchmany (oursqlx/oursql.c:21567)()

oursqlx/cursor.pyx in oursql.Cursor._check_statements (oursqlx/oursql.c:21103)()

ProgrammingError: (None, 'no results available', None)
# It seems I'd have to catch & check for ``exception.args[1] == 'no results available'``
# to answer Question 2.
# Checking solely for `ProgrammingError` would probably be too generic
# and would risk swallowing other types of errors besides "no results available."

After considering this second example, I think that one or more of the following enhancements might be nice to have:

  • Change nextset() to always return None when there are no more result sets, instead of sometimes raising ProgrammingError when the cursor is in its "already past the end" state.
  • Raise a more specific Exception for the case of 'no results available.' Maybe named something like NoResults? Could it perhaps even be a subclass of StopIteration?
  • Make some kind of promise somewhere, either in docs or via a publicly exposed code constant, that the string value 'no more results' is safe to test against in the long term.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions