Skip to content

Commit 82ad22a

Browse files
author
Erlend Egeberg Aasland
authored
bpo-42213: Check connection in sqlite3.Connection.__enter__ (GH-26512)
Try to harden connection close: - add tests that exercise stuff against a closed database - add wrapper for sqlite3_close_v2() - check connection on __enter__ - explicitly free pending statements before close() - sqlite3_close_v2() always returns SQLITE_OK
1 parent 937cebc commit 82ad22a

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

Lib/sqlite3/test/dbapi.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,26 @@ def test_failed_open(self):
135135
def test_close(self):
136136
self.cx.close()
137137

138+
def test_use_after_close(self):
139+
sql = "select 1"
140+
cu = self.cx.cursor()
141+
res = cu.execute(sql)
142+
self.cx.close()
143+
self.assertRaises(sqlite.ProgrammingError, res.fetchall)
144+
self.assertRaises(sqlite.ProgrammingError, cu.execute, sql)
145+
self.assertRaises(sqlite.ProgrammingError, cu.executemany, sql, [])
146+
self.assertRaises(sqlite.ProgrammingError, cu.executescript, sql)
147+
self.assertRaises(sqlite.ProgrammingError, self.cx.execute, sql)
148+
self.assertRaises(sqlite.ProgrammingError,
149+
self.cx.executemany, sql, [])
150+
self.assertRaises(sqlite.ProgrammingError, self.cx.executescript, sql)
151+
self.assertRaises(sqlite.ProgrammingError,
152+
self.cx.create_function, "t", 1, lambda x: x)
153+
self.assertRaises(sqlite.ProgrammingError, self.cx.cursor)
154+
with self.assertRaises(sqlite.ProgrammingError):
155+
with self.cx:
156+
pass
157+
138158
def test_exceptions(self):
139159
# Optional DB-API extension.
140160
self.assertEqual(self.cx.Warning, sqlite.Warning)

Modules/_sqlite/connection.c

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,16 @@ connection_clear(pysqlite_Connection *self)
258258
return 0;
259259
}
260260

261+
static void
262+
connection_close(pysqlite_Connection *self)
263+
{
264+
if (self->db) {
265+
int rc = sqlite3_close_v2(self->db);
266+
assert(rc == SQLITE_OK);
267+
self->db = NULL;
268+
}
269+
}
270+
261271
static void
262272
connection_dealloc(pysqlite_Connection *self)
263273
{
@@ -266,9 +276,7 @@ connection_dealloc(pysqlite_Connection *self)
266276
tp->tp_clear((PyObject *)self);
267277

268278
/* Clean up if user has not called .close() explicitly. */
269-
if (self->db) {
270-
sqlite3_close_v2(self->db);
271-
}
279+
connection_close(self);
272280

273281
tp->tp_free(self);
274282
Py_DECREF(tp);
@@ -353,24 +361,12 @@ static PyObject *
353361
pysqlite_connection_close_impl(pysqlite_Connection *self)
354362
/*[clinic end generated code: output=a546a0da212c9b97 input=3d58064bbffaa3d3]*/
355363
{
356-
int rc;
357-
358364
if (!pysqlite_check_thread(self)) {
359365
return NULL;
360366
}
361367

362368
pysqlite_do_all_statements(self, ACTION_FINALIZE, 1);
363-
364-
if (self->db) {
365-
rc = sqlite3_close_v2(self->db);
366-
367-
if (rc != SQLITE_OK) {
368-
_pysqlite_seterror(self->db);
369-
return NULL;
370-
} else {
371-
self->db = NULL;
372-
}
373-
}
369+
connection_close(self);
374370

375371
Py_RETURN_NONE;
376372
}
@@ -1820,6 +1816,9 @@ static PyObject *
18201816
pysqlite_connection_enter_impl(pysqlite_Connection *self)
18211817
/*[clinic end generated code: output=457b09726d3e9dcd input=127d7a4f17e86d8f]*/
18221818
{
1819+
if (!pysqlite_check_connection(self)) {
1820+
return NULL;
1821+
}
18231822
return Py_NewRef((PyObject *)self);
18241823
}
18251824

0 commit comments

Comments
 (0)