Skip to content

Commit

Permalink
gh-101693: In sqlite3, deprecate using named placeholders with parame…
Browse files Browse the repository at this point in the history
…ters supplied as a sequence (#101698)
  • Loading branch information
erlend-aasland committed Feb 15, 2023
1 parent d777790 commit 8a2b7ee
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 1 deletion.
19 changes: 18 additions & 1 deletion Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1442,6 +1442,14 @@ Cursor objects
and there is no open transaction,
a transaction is implicitly opened before executing *sql*.

.. deprecated-removed:: 3.12 3.14

:exc:`DeprecationWarning` is emitted if
:ref:`named placeholders <sqlite3-placeholders>` are used
and *parameters* is a sequence instead of a :class:`dict`.
Starting with Python 3.14, :exc:`ProgrammingError` will
be raised instead.

Use :meth:`executescript` to execute multiple SQL statements.

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

.. deprecated-removed:: 3.12 3.14

:exc:`DeprecationWarning` is emitted if
:ref:`named placeholders <sqlite3-placeholders>` are used
and the items in *parameters* are sequences
instead of :class:`dict`\s.
Starting with Python 3.14, :exc:`ProgrammingError` will
be raised instead.

.. method:: executescript(sql_script, /)

Execute the SQL statements in *sql_script*.
Expand Down Expand Up @@ -1971,7 +1988,7 @@ question marks (qmark style) or named placeholders (named style).
For the qmark style, *parameters* must be a
:term:`sequence` whose length must match the number of placeholders,
or a :exc:`ProgrammingError` is raised.
For the named style, *parameters* should be
For the named style, *parameters* must be
an instance of a :class:`dict` (or a subclass),
which must contain keys for all named parameters;
any extra items are ignored.
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ Deprecated
and tailor them to your needs.
(Contributed by Erlend E. Aasland in :gh:`90016`.)

* In :meth:`~sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted
when :ref:`named placeholders <sqlite3-placeholders>` are used together with
parameters supplied as a :term:`sequence` instead of as a :class:`dict`.
Starting from Python 3.14, using named placeholders with parameters supplied
as a sequence will raise a :exc:`~sqlite3.ProgrammingError`.
(Contributed by Erlend E. Aasland in :gh:`101698`.)

* The 3-arg signatures (type, value, traceback) of :meth:`~coroutine.throw`,
:meth:`~generator.throw` and :meth:`~agen.athrow` are deprecated and
may be removed in a future version of Python. Use the single-arg versions
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,21 @@ def __getitem__(slf, x):
with self.assertRaises(ZeroDivisionError):
self.cu.execute("select name from test where name=?", L())

def test_execute_named_param_and_sequence(self):
dataset = (
("select :a", (1,)),
("select :a, ?, ?", (1, 2, 3)),
("select ?, :b, ?", (1, 2, 3)),
("select ?, ?, :c", (1, 2, 3)),
("select :a, :b, ?", (1, 2, 3)),
)
msg = "Binding.*is a named parameter"
for query, params in dataset:
with self.subTest(query=query, params=params):
with self.assertWarnsRegex(DeprecationWarning, msg) as cm:
self.cu.execute(query, params)
self.assertEqual(cm.filename, __file__)

def test_execute_too_many_params(self):
category = sqlite.SQLITE_LIMIT_VARIABLE_NUMBER
msg = "too many SQL variables"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
In :meth:`sqlite3.Cursor.execute`, :exc:`DeprecationWarning` is now emitted
when :ref:`named placeholders <sqlite3-placeholders>` are used together with
parameters supplied as a :term:`sequence` instead of as a :class:`dict`.
Starting from Python 3.14, using named placeholders with parameters supplied
as a sequence will raise a :exc:`~sqlite3.ProgrammingError`.
Patch by Erlend E. Aasland.
13 changes: 13 additions & 0 deletions Modules/_sqlite/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,19 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
return;
}
for (i = 0; i < num_params; i++) {
const char *name = sqlite3_bind_parameter_name(self->st, i+1);
if (name != NULL) {
int ret = PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"Binding %d ('%s') is a named parameter, but you "
"supplied a sequence which requires nameless (qmark) "
"placeholders. Starting with Python 3.14 an "
"sqlite3.ProgrammingError will be raised.",
i+1, name);
if (ret < 0) {
return;
}
}

if (PyTuple_CheckExact(parameters)) {
PyObject *item = PyTuple_GET_ITEM(parameters, i);
current_param = Py_NewRef(item);
Expand Down

0 comments on commit 8a2b7ee

Please sign in to comment.