Skip to content

Commit aa66807

Browse files
authored
gh-146090: fix memory management of internal sqlite3 callback contexts (#146569)
1 parent 6932c3e commit aa66807

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
@@ -1059,13 +1059,16 @@ static callback_context *
10591059
create_callback_context(PyTypeObject *cls, PyObject *callable)
10601060
{
10611061
callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
1062-
if (ctx != NULL) {
1063-
PyObject *module = PyType_GetModule(cls);
1064-
ctx->refcount = 1;
1065-
ctx->callable = Py_NewRef(callable);
1066-
ctx->module = Py_NewRef(module);
1067-
ctx->state = pysqlite_get_state(module);
1062+
if (ctx == NULL) {
1063+
PyErr_NoMemory();
1064+
return NULL;
10681065
}
1066+
1067+
PyObject *module = PyType_GetModule(cls);
1068+
ctx->refcount = 1;
1069+
ctx->callable = Py_NewRef(callable);
1070+
ctx->module = Py_NewRef(module);
1071+
ctx->state = pysqlite_get_state(module);
10691072
return ctx;
10701073
}
10711074

@@ -2198,7 +2201,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
21982201
* the context before returning.
21992202
*/
22002203
if (callable != Py_None) {
2201-
free_callback_context(ctx);
2204+
decref_callback_context(ctx);
22022205
}
22032206
set_error_from_db(self->state, self->db);
22042207
return NULL;

0 commit comments

Comments
 (0)