Skip to content

Commit 8a2b7ee

Browse files
gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied as a sequence (#101698)
1 parent d777790 commit 8a2b7ee

File tree

5 files changed

+59
-1
lines changed

5 files changed

+59
-1
lines changed

Doc/library/sqlite3.rst

+18-1
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,14 @@ Cursor objects
14421442
and there is no open transaction,
14431443
a transaction is implicitly opened before executing *sql*.
14441444

1445+
.. deprecated-removed:: 3.12 3.14
1446+
1447+
:exc:`DeprecationWarning` is emitted if
1448+
:ref:`named placeholders <sqlite3-placeholders>` are used
1449+
and *parameters* is a sequence instead of a :class:`dict`.
1450+
Starting with Python 3.14, :exc:`ProgrammingError` will
1451+
be raised instead.
1452+
14451453
Use :meth:`executescript` to execute multiple SQL statements.
14461454

14471455
.. method:: executemany(sql, parameters, /)
@@ -1476,6 +1484,15 @@ Cursor objects
14761484
# cur is an sqlite3.Cursor object
14771485
cur.executemany("INSERT INTO data VALUES(?)", rows)
14781486

1487+
.. deprecated-removed:: 3.12 3.14
1488+
1489+
:exc:`DeprecationWarning` is emitted if
1490+
:ref:`named placeholders <sqlite3-placeholders>` are used
1491+
and the items in *parameters* are sequences
1492+
instead of :class:`dict`\s.
1493+
Starting with Python 3.14, :exc:`ProgrammingError` will
1494+
be raised instead.
1495+
14791496
.. method:: executescript(sql_script, /)
14801497

14811498
Execute the SQL statements in *sql_script*.
@@ -1971,7 +1988,7 @@ question marks (qmark style) or named placeholders (named style).
19711988
For the qmark style, *parameters* must be a
19721989
:term:`sequence` whose length must match the number of placeholders,
19731990
or a :exc:`ProgrammingError` is raised.
1974-
For the named style, *parameters* should be
1991+
For the named style, *parameters* must be
19751992
an instance of a :class:`dict` (or a subclass),
19761993
which must contain keys for all named parameters;
19771994
any extra items are ignored.

Doc/whatsnew/3.12.rst

+7
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,13 @@ Deprecated
415415
and tailor them to your needs.
416416
(Contributed by Erlend E. Aasland in :gh:`90016`.)
417417

418+
* In :meth:`~sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted
419+
when :ref:`named placeholders <sqlite3-placeholders>` are used together with
420+
parameters supplied as a :term:`sequence` instead of as a :class:`dict`.
421+
Starting from Python 3.14, using named placeholders with parameters supplied
422+
as a sequence will raise a :exc:`~sqlite3.ProgrammingError`.
423+
(Contributed by Erlend E. Aasland in :gh:`101698`.)
424+
418425
* The 3-arg signatures (type, value, traceback) of :meth:`~coroutine.throw`,
419426
:meth:`~generator.throw` and :meth:`~agen.athrow` are deprecated and
420427
may be removed in a future version of Python. Use the single-arg versions

Lib/test/test_sqlite3/test_dbapi.py

+15
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,21 @@ def __getitem__(slf, x):
861861
with self.assertRaises(ZeroDivisionError):
862862
self.cu.execute("select name from test where name=?", L())
863863

864+
def test_execute_named_param_and_sequence(self):
865+
dataset = (
866+
("select :a", (1,)),
867+
("select :a, ?, ?", (1, 2, 3)),
868+
("select ?, :b, ?", (1, 2, 3)),
869+
("select ?, ?, :c", (1, 2, 3)),
870+
("select :a, :b, ?", (1, 2, 3)),
871+
)
872+
msg = "Binding.*is a named parameter"
873+
for query, params in dataset:
874+
with self.subTest(query=query, params=params):
875+
with self.assertWarnsRegex(DeprecationWarning, msg) as cm:
876+
self.cu.execute(query, params)
877+
self.assertEqual(cm.filename, __file__)
878+
864879
def test_execute_too_many_params(self):
865880
category = sqlite.SQLITE_LIMIT_VARIABLE_NUMBER
866881
msg = "too many SQL variables"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
In :meth:`sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted
2+
when :ref:`named placeholders <sqlite3-placeholders>` are used together with
3+
parameters supplied as a :term:`sequence` instead of as a :class:`dict`.
4+
Starting from Python 3.14, using named placeholders with parameters supplied
5+
as a sequence will raise a :exc:`~sqlite3.ProgrammingError`.
6+
Patch by Erlend E. Aasland.

Modules/_sqlite/cursor.c

+13
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,19 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
662662
return;
663663
}
664664
for (i = 0; i < num_params; i++) {
665+
const char *name = sqlite3_bind_parameter_name(self->st, i+1);
666+
if (name != NULL) {
667+
int ret = PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
668+
"Binding %d ('%s') is a named parameter, but you "
669+
"supplied a sequence which requires nameless (qmark) "
670+
"placeholders. Starting with Python 3.14 an "
671+
"sqlite3.ProgrammingError will be raised.",
672+
i+1, name);
673+
if (ret < 0) {
674+
return;
675+
}
676+
}
677+
665678
if (PyTuple_CheckExact(parameters)) {
666679
PyObject *item = PyTuple_GET_ITEM(parameters, i);
667680
current_param = Py_NewRef(item);

0 commit comments

Comments
 (0)