]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-101693: In sqlite3, deprecate using named placeholders with parameters supplied...
authorErlend E. Aasland <erlend.aasland@protonmail.com>
Wed, 15 Feb 2023 05:27:16 +0000 (06:27 +0100)
committerGitHub <noreply@github.com>
Wed, 15 Feb 2023 05:27:16 +0000 (06:27 +0100)
Doc/library/sqlite3.rst
Doc/whatsnew/3.12.rst
Lib/test/test_sqlite3/test_dbapi.py
Misc/NEWS.d/next/Library/2023-02-08-18-20-58.gh-issue-101693.4_LPXj.rst [new file with mode: 0644]
Modules/_sqlite/cursor.c

index 8ffc0aad91995c7d63ed5eff43a4eecbdc549d4a..18d0a5e630f6a9bf14d4b55539759f7828e48ad9 100644 (file)
@@ -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.
index 45a5e5062d55b620df3a5d36e2a54140c94c55f9..c62f462a19a2df1e9b63795012270b5dfaba6c19 100644 (file)
@@ -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
index 363a308f3e5feca780adea083f1108ffcd519413..695e213cdc7b752b4130b6ae84cf64d6c9fba008 100644 (file)
@@ -861,6 +861,21 @@ class CursorTests(unittest.TestCase):
         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 (file)
index 0000000..e436054
--- /dev/null
@@ -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.
index a4e22bb4a2b58d71bb6ed7da01b03e4c05537a8f..6f7970cf8197a252a589d63ee00a0d67bee16501 100644 (file)
@@ -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);