This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author erlendaasland
Recipients berker.peksag, corona10, erlendaasland, serhiy.storchaka
Date 2021-02-24.22:52:56
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1614207176.29.0.581075020903.issue43290@roundup.psfhosted.org>
In-reply-to
Content
pysqlite_cursor_iternext() has four users:
- sqlite3.Cursor.fetchone()
- sqlite3.Cursor.fetchall()
- sqlite3.Cursor.fetchmany()
- sqlite3.Cursor.__next__()

All of these methods pass self to pysqlite_cursor_iternext().

pysqlite_cursor_iternext() starts by checking the state of the cursor (is it initialised, it the database open, etc.). If there is a Statement object, pysqlite_step() is called with the sqlite3_stmt pointer from the Statement object (Cursor->statement->st).

The statement pointer of the Cursor object is set in _pysqlite_query_execute() – the last pysqlite_step() user – either from the LRU cache (line 470), or by creating a new Statement object (line 479). The latter only leaves a valid Cursor->statement->st pointer (sqlite3_stmt pointer) if the Statement object was successfully created, and the sqlite3_stmt successfully prepared. (I assume only valid Statement objects are added to the cache.) Before the main loop of _pysqlite_query_execute() starts, the statement is reset. In the loop, the next parameter set is fetched, the statement is (re)bound, and step is called. If Cursor.execute() called _pysqlite_query_execute(), the parameter list is initialised to a single-item list, and the loop is only run once. From what I can read, this function is also safe. (But it is very messy; for instance, if there's an active Statement, it is reset twice before the loop starts.)

I tried forcing an error by using an uninitialised cursor:
>>> cx = sqlite3.connect(":memory:")
>>> cu = sqlite3.Cursor.__new__(sqlite3.Cursor)
>>> sqlite3.Cursor.fetchone(cu)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
sqlite3.ProgrammingError: Base Cursor.__init__ not called.
>>> next(cu)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
sqlite3.ProgrammingError: Base Cursor.__init__ not called.

Ditto for fetchmany() and fetchall(). This is consistent with current behaviour.

Calling fetch*() without first executing a statement:
>>> cu = cx.cursor()
>>> cu.fetchone()
>>> cu.fetchmany()
[]
>>> cu.fetchall()
[]
>>> next(cu)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

This is consistent with current behaviour.



I might have missed something, but from what I can see, there are no paths that lead to pysqlite_step() being called with a NULL pointer.

Berker, Serhiy, please correct me if I'm wrong.
History
Date User Action Args
2021-02-24 22:52:56erlendaaslandsetrecipients: + erlendaasland, berker.peksag, serhiy.storchaka, corona10
2021-02-24 22:52:56erlendaaslandsetmessageid: <1614207176.29.0.581075020903.issue43290@roundup.psfhosted.org>
2021-02-24 22:52:56erlendaaslandlinkissue43290 messages
2021-02-24 22:52:56erlendaaslandcreate