Skip to content

Commit 0b419b7

Browse files
bpo-41662: Fix bugs in binding parameters in sqlite3 (pythonGH-21998)
* When the parameters argument is a list, correctly handle the case of changing it during iteration. * When the parameters argument is a custom sequence, no longer override an exception raised in ``__len__()``.
1 parent dcfaa52 commit 0b419b7

File tree

5 files changed

+34
-3
lines changed

5 files changed

+34
-3
lines changed

Lib/sqlite3/test/dbapi.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def CheckExecuteParamList(self):
270270
self.assertEqual(row[0], "foo")
271271

272272
def CheckExecuteParamSequence(self):
273-
class L(object):
273+
class L:
274274
def __len__(self):
275275
return 1
276276
def __getitem__(self, x):
@@ -282,6 +282,18 @@ def __getitem__(self, x):
282282
row = self.cu.fetchone()
283283
self.assertEqual(row[0], "foo")
284284

285+
def CheckExecuteParamSequenceBadLen(self):
286+
# Issue41662: Error in __len__() was overridden with ProgrammingError.
287+
class L:
288+
def __len__(self):
289+
1/0
290+
def __getitem__(slf, x):
291+
raise AssertionError
292+
293+
self.cu.execute("insert into test(name) values ('foo')")
294+
with self.assertRaises(ZeroDivisionError):
295+
self.cu.execute("select name from test where name=?", L())
296+
285297
def CheckExecuteDictMapping(self):
286298
self.cu.execute("insert into test(name) values ('foo')")
287299
self.cu.execute("select name from test where name=:name", {"name": "foo"})

Lib/sqlite3/test/regression.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,19 @@ def CheckTypeMapUsage(self):
132132
con.execute("insert into foo(bar) values (5)")
133133
con.execute(SELECT)
134134

135+
def CheckBindMutatingList(self):
136+
# Issue41662: Crash when mutate a list of parameters during iteration.
137+
class X:
138+
def __conform__(self, protocol):
139+
parameters.clear()
140+
return "..."
141+
parameters = [X(), 0]
142+
con = sqlite.connect(":memory:",detect_types=sqlite.PARSE_DECLTYPES)
143+
con.execute("create table foo(bar X, baz integer)")
144+
# Should not crash
145+
with self.assertRaises(IndexError):
146+
con.execute("insert into foo(bar, baz) values (?, ?)", parameters)
147+
135148
def CheckErrorMsgDecodeError(self):
136149
# When porting the module to Python 3.0, the error message about
137150
# decoding errors disappeared. This verifies they're back again.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed crash when mutate list of parameters during iteration in :mod:`sqlite3`.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
No longer override exceptions raised in ``__len__()`` of a sequence of
2+
parameters in :mod:`sqlite3` with :exc:`~sqlite3.ProgrammingError`.

Modules/_sqlite/statement.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para
227227
num_params = PyList_GET_SIZE(parameters);
228228
} else {
229229
num_params = PySequence_Size(parameters);
230+
if (num_params == -1) {
231+
return;
232+
}
230233
}
231234
if (num_params != num_params_needed) {
232235
PyErr_Format(pysqlite_ProgrammingError,
@@ -238,9 +241,9 @@ void pysqlite_statement_bind_parameters(pysqlite_Statement* self, PyObject* para
238241
for (i = 0; i < num_params; i++) {
239242
if (PyTuple_CheckExact(parameters)) {
240243
current_param = PyTuple_GET_ITEM(parameters, i);
241-
Py_XINCREF(current_param);
244+
Py_INCREF(current_param);
242245
} else if (PyList_CheckExact(parameters)) {
243-
current_param = PyList_GET_ITEM(parameters, i);
246+
current_param = PyList_GetItem(parameters, i);
244247
Py_XINCREF(current_param);
245248
} else {
246249
current_param = PySequence_GetItem(parameters, i);

0 commit comments

Comments
 (0)