First, what is a python sqlite3.Cursor? The
code
shows it is essentially a holder for a sqlite3_stmt structure, a prepared
statement handle which is passed to
sqlite3_step to iterate through
result rows.
As far as I can tell, there’s no benefit to creating your own cursor object
with conn.cursor() over just using the one automatically created for you
and returned by
conn.execute().
Skip conn.cursor() and save yourself the extra line of code each time.
(This is sqlite-specific advice; there may be reasons to create your own
cursors with other DB-API drivers for other database systems, I don’t
know.)
What does cursor.close() do?
static PyObject *
pysqlite_cursor_close_impl(pysqlite_Cursor *self)
{
    ⋮
    if (self->statement) {
    (void)stmt_reset(self->statement);
    Py_CLEAR(self->statement);
    ⋮
}
The sqlite
docs
give a short description of sqlite3_reset, which is what stmt_reset calls:
6. Binding Parameters and Reusing Prepared Statements
… SQLite allows the same prepared statement to be evaluated multiple
times. This is accomplished using the following routines:
After a prepared statement has been evaluated by one or more calls to
sqlite3_step(), it can be reset in order to be evaluated again by a call
to sqlite3_reset(). Think of sqlite3_reset() as rewinding the
prepared statement program back to the beginning.
Closing a cursor causes Python to tell the underlying sqlite library to
discard the associated result set. But Python still keeps the
sqlite3_stmt structure around, because Python internally maintains a
cache of prepared statements.
Code like the following is generally fine on CPython:
for row in conn.execute("SELECT …"):
    do_stuff_with(r)
This is because:
- When iteration gets to the end of the result set, python will
automatically call - stmt_resetto discard the result
set.
This happens with both standard python iteration protocols like- for … in …, and the- sqlite3.Cursor.fetch*methods.
 
- If the loop exits early, e.g., because an exception was raised,
CPython’s reference-counting will trigger finalization of the anonymous cursor
object, again calling - stmt_reset. Other python implementations may differ.
 
Given the above, while it is unlikely that you will ever run into a memory leak by not
closing your cursors, it is definitely possible if you have cursors that
are both:
- only partially-iterated, and
- kept in scope for long periods of time.
Thus for portability and explicitness you may want to write:
from contextlib import closing
with closing(conn.execute("SELECT …")) as cursor:
    for row in cursor:
        …