From cc6c638b7553dceba41352d8fdf1919674918937 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sun, 26 Jul 2026 13:02:25 +0900 Subject: [PATCH] sqlite3: raise ProgrammingError when named param receives sequence (#8363) CPython raises ProgrammingError with "Binding N is a named parameter" when a query uses named placeholders (, , ) but the caller passes a sequence instead of a mapping. RustPython's bind_parameters_sequence() did not check whether each binding slot is a named parameter, so no error was raised. Fix: call sqlite3_bind_parameter_name() for each slot in bind_parameters_sequence(). If the first byte of the returned name is not '?' (i.e. it is a named placeholder), raise ProgrammingError before attempting to bind. Assisted-by: GitHub Copilot:claude-sonnet-4-6 --- .cspell.json | 1 + Lib/test/test_sqlite3/test_dbapi.py | 1 - crates/stdlib/src/_sqlite3.rs | 10 ++++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.cspell.json b/.cspell.json index ab3d229cf..21d401563 100644 --- a/.cspell.json +++ b/.cspell.json @@ -83,6 +83,7 @@ "opargs", "pointee", "pyc", + "qmark", "reborrow", "reborrows", "reparenting", diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index c7ea5b59e..68f8969a0 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -875,7 +875,6 @@ class CursorTests(unittest.TestCase): with self.assertRaises(ZeroDivisionError): self.cu.execute("select name from test where name=?", L()) - @unittest.expectedFailure # TODO: RUSTPYTHON; mixed named and positional parameters not validated def test_execute_named_param_and_sequence(self): dataset = ( ("select :a", (1,)), diff --git a/crates/stdlib/src/_sqlite3.rs b/crates/stdlib/src/_sqlite3.rs index 5f15b0cbc..58d8ad7b3 100644 --- a/crates/stdlib/src/_sqlite3.rs +++ b/crates/stdlib/src/_sqlite3.rs @@ -3208,6 +3208,16 @@ mod _sqlite3 { } for i in 1..=num_needed { + let name = unsafe { sqlite3_bind_parameter_name(self.st, i) }; + if !name.is_null() && unsafe { *name } != b'?' as libc::c_char { + let name_str = ptr_to_str(name, vm)?; + return Err(new_programming_error( + vm, + format!( + "Binding {i} ('{name_str}') is a named parameter, but you supplied a sequence which requires nameless (qmark) placeholders." + ), + )); + } let val = seq.get_item(i as isize - 1, vm)?; self.bind_parameter(i, &val, vm)?; }