[Python-checkins] gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied as a sequence (#101698)
erlend-aasland
webhook-mailer at python.org
Wed Feb 15 00:27:25 EST 2023
https://github.com/python/cpython/commit/8a2b7ee64d1bde762438b458ea7fe88f054a3a88
commit: 8a2b7ee64d1bde762438b458ea7fe88f054a3a88
branch: main
author: Erlend E. Aasland <erlend.aasland at protonmail.com>
committer: erlend-aasland <erlend.aasland at protonmail.com>
date: 2023-02-15T06:27:16+01:00
summary:
gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied as a sequence (#101698)
files:
A Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst
M Doc/library/sqlite3.rst
M Doc/whatsnew/3.12.rst
M Lib/test/test_sqlite3/test_dbapi.py
M Modules/_sqlite/cursor.c
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 8ffc0aad9199..18d0a5e630f6 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -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, /)
@@ -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*.
@@ -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.
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 45a5e5062d55..c62f462a19a2 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -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
diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py
index 363a308f3e5f..695e213cdc7b 100644
--- a/Lib/test/test_sqlite3/test_dbapi.py
+++ b/Lib/test/test_sqlite3/test_dbapi.py
@@ -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"
diff --git a/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst
new file mode 100644
index 000000000000..e436054b15b6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst
@@ -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.
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index a4e22bb4a2b5..6f7970cf8197 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -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);
More information about the Python-checkins
mailing list