Skip to content

Commit 6fdf084

Browse files
[3.13] gh-146090: fix memory management of internal sqlite3 callback contexts (GH-146569) (#146596)
gh-146090: fix memory management of internal `sqlite3` callback contexts (GH-146569) (cherry picked from commit aa66807) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 59f33e8 commit 6fdf084

File tree

4 files changed

+30
-7
lines changed

4 files changed

+30
-7
lines changed

Lib/test/test_sqlite3/test_hooks.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@ def test_collation_register_twice(self):
120120
self.assertEqual(result[0][0], 'b')
121121
self.assertEqual(result[1][0], 'a')
122122

123+
def test_collation_register_when_busy(self):
124+
# See https://github.com/python/cpython/issues/146090.
125+
con = self.con
126+
con.create_collation("mycoll", lambda x, y: (x > y) - (x < y))
127+
con.execute("CREATE TABLE t(x TEXT)")
128+
con.execute("INSERT INTO t VALUES (?)", ("a",))
129+
con.execute("INSERT INTO t VALUES (?)", ("b",))
130+
con.commit()
131+
132+
cursor = self.con.execute("SELECT x FROM t ORDER BY x COLLATE mycoll")
133+
next(cursor)
134+
# Replace the collation while the statement is active -> SQLITE_BUSY.
135+
with self.assertRaises(sqlite.OperationalError) as cm:
136+
self.con.create_collation("mycoll", lambda a, b: 0)
137+
123138
def test_deregister_collation(self):
124139
"""
125140
Register a collation, then deregister it. Make sure an error is raised if we try
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`sqlite3`: properly raise :exc:`MemoryError` instead of :exc:`SystemError`
2+
when a context callback fails to be allocated. Patch by Bénédikt Tran.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`sqlite3`: fix a crash when :meth:`sqlite3.Connection.create_collation`
2+
fails with `SQLITE_BUSY <https://sqlite.org/rescode.html#busy>`__. Patch by
3+
Bénédikt Tran.

Modules/_sqlite/connection.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,13 +1103,16 @@ static callback_context *
11031103
create_callback_context(PyTypeObject *cls, PyObject *callable)
11041104
{
11051105
callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
1106-
if (ctx != NULL) {
1107-
PyObject *module = PyType_GetModule(cls);
1108-
ctx->refcount = 1;
1109-
ctx->callable = Py_NewRef(callable);
1110-
ctx->module = Py_NewRef(module);
1111-
ctx->state = pysqlite_get_state(module);
1106+
if (ctx == NULL) {
1107+
PyErr_NoMemory();
1108+
return NULL;
11121109
}
1110+
1111+
PyObject *module = PyType_GetModule(cls);
1112+
ctx->refcount = 1;
1113+
ctx->callable = Py_NewRef(callable);
1114+
ctx->module = Py_NewRef(module);
1115+
ctx->state = pysqlite_get_state(module);
11131116
return ctx;
11141117
}
11151118

@@ -2212,7 +2215,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
22122215
* the context before returning.
22132216
*/
22142217
if (callable != Py_None) {
2215-
free_callback_context(ctx);
2218+
decref_callback_context(ctx);
22162219
}
22172220
_pysqlite_seterror(self->state, self->db);
22182221
return NULL;

0 commit comments

Comments
 (0)