Skip to content

Commit 6f07252

Browse files
committed
gh-145040: Fix close_attempted_in_callback flag consumed by nested callbacks
Only check and consume the close_attempted_in_callback flag when in_callback reaches zero (the outermost level). Previously, a nested stmt_step() inside a callback could consume the flag, causing the outermost caller to miss the error.
1 parent eb69460 commit 6f07252

File tree

2 files changed

+33
-2
lines changed

2 files changed

+33
-2
lines changed

Lib/test/test_sqlite3/test_userfunctions.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,33 @@ def inner_func(x):
797797
self.assertEqual(con.execute("SELECT 1").fetchone(), (1,))
798798
con.close()
799799

800+
def test_close_conn_in_nested_callback_caught(self):
801+
# gh-145040: close attempt must propagate even if the exception
802+
# is caught inside the callback and a nested execute consumes
803+
# the flag.
804+
con = sqlite.connect(":memory:", autocommit=True)
805+
con.execute("CREATE TABLE t(x INTEGER)")
806+
con.execute("INSERT INTO t VALUES(1)")
807+
808+
def swallow_close(x):
809+
try:
810+
con.close()
811+
except sqlite.ProgrammingError:
812+
pass
813+
try:
814+
con.execute("SELECT 1")
815+
except sqlite.ProgrammingError:
816+
pass
817+
return x
818+
819+
con.create_function("swallow_close", 1, swallow_close)
820+
msg = "from within a callback"
821+
with self.assertRaisesRegex(sqlite.ProgrammingError, msg):
822+
con.execute("SELECT swallow_close(x) FROM t")
823+
# Connection must still be usable.
824+
self.assertEqual(con.execute("SELECT 1").fetchone(), (1,))
825+
con.close()
826+
800827
def test_close_conn_in_udf_during_executemany(self):
801828
# gh-145040: closing connection in UDF during executemany.
802829
con = sqlite.connect(":memory:", autocommit=True)

Modules/_sqlite/cursor.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,9 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
908908
self->connection->in_callback++;
909909
rc = stmt_step(self->statement->st);
910910
self->connection->in_callback--;
911-
if (self->connection->close_attempted_in_callback) {
911+
if (self->connection->close_attempted_in_callback
912+
&& self->connection->in_callback == 0)
913+
{
912914
self->connection->close_attempted_in_callback = 0;
913915
PyErr_Clear();
914916
PyErr_SetString(state->ProgrammingError,
@@ -1168,7 +1170,9 @@ pysqlite_cursor_iternext(PyObject *op)
11681170
self->connection->in_callback++;
11691171
int rc = stmt_step(stmt);
11701172
self->connection->in_callback--;
1171-
if (self->connection->close_attempted_in_callback) {
1173+
if (self->connection->close_attempted_in_callback
1174+
&& self->connection->in_callback == 0)
1175+
{
11721176
self->connection->close_attempted_in_callback = 0;
11731177
Py_DECREF(row);
11741178
Py_CLEAR(self->statement);

0 commit comments

Comments
 (0)