Repository: duckdb/duckdb (language: python)

Issue:
Exception Raised when trying to EXPLAIN ANALYZE with parameters
### What happens?

When I `execute` the query `EXPLAIN ANALYZE SELECT * FROM my_table WHERE name = ?` with parameters I get `Error: Invalid Input Error: Parameter argument/count mismatch, identifiers of the excess parameters: 1`.

If I have to remove the parameters to make a sample query string it will take a long time. Thus please make parameters work.

I can see this is the same issue as in Javascript reported in the closed issue https://github.com/duckdb/duckdb/issues/9393.

### To Reproduce

```python
import duckdb

# Create a connection
con = duckdb.connect()

# Create a cursor
cur = con.cursor()

# Create a table
cur.execute("CREATE TABLE my_table (id INTEGER, name VARCHAR)")

# Insert some data
cur.execute("INSERT INTO my_table VALUES (1, 'John'), (2, 'Jane'), (3, 'Doe')")

# Execute a parameterized query
query = "EXPLAIN ANALYZE SELECT * FROM my_table WHERE name = ?"
params = ("John",)
cur.execute(query, params)

# Fetch the results
results = cur.fetchall()
print(results)

# Close the cursor and connection
cur.close()
con.close()
```

```bash
Traceback (most recent call last):
  File "/home/jovyan/repos/mt-pm-reporting/script.py", line 18, in <module>
    cur.execute(query, params)
duckdb.duckdb.InvalidInputException: Invalid Input Error: Attempting to execute an unsuccessful or closed pending query result
Error: Invalid Input Error: Parameter argument/count mismatch, identifiers of the excess parameters: 1
```

### OS:

x86

### DuckDB Version:

??

### DuckDB Client:

duckdb==1.0.0 in python

### Full Name:

Marc Skov Madsen

### Affiliation:

Ørsted

### What is the latest build you tested with? If possible, we recommend testing with the latest nightly build.

I have not tested with any build

### Did you include all relevant data sets for reproducing the issue?

Not applicable - the reproduction does not require a data set

### Did you include all code required to reproduce the issue?

- [X] Yes, I have

### Did you include all relevant configuration (e.g., CPU architecture, Python version, Linux distribution) to reproduce the issue?

- [X] Yes, I have


Discussion / hints:

Fix patch:
diff --git a/src/include/duckdb/parser/transformer.hpp b/src/include/duckdb/parser/transformer.hpp
index ed7ae9c57cfc..0646660ce7e9 100644
--- a/src/include/duckdb/parser/transformer.hpp
+++ b/src/include/duckdb/parser/transformer.hpp
@@ -90,6 +90,7 @@ class Transformer {
 	Transformer &RootTransformer();
 	const Transformer &RootTransformer() const;
 	void SetParamCount(idx_t new_count);
+	void ClearParameters();
 	void SetParam(const string &name, idx_t index, PreparedParamType type);
 	bool GetParam(const string &name, idx_t &index, PreparedParamType type);
 
diff --git a/src/parser/transform/statement/transform_prepare.cpp b/src/parser/transform/statement/transform_prepare.cpp
index 1979987699c5..5466731277b5 100644
--- a/src/parser/transform/statement/transform_prepare.cpp
+++ b/src/parser/transform/statement/transform_prepare.cpp
@@ -15,7 +15,7 @@ unique_ptr<PrepareStatement> Transformer::TransformPrepare(duckdb_libpgquery::PG
 	auto result = make_uniq<PrepareStatement>();
 	result->name = string(stmt.name);
 	result->statement = TransformStatement(*stmt.query);
-	SetParamCount(0);
+	ClearParameters();
 
 	return result;
 }
diff --git a/src/parser/transformer.cpp b/src/parser/transformer.cpp
index 23b5d2ccb2c1..6a30a9d334e1 100644
--- a/src/parser/transformer.cpp
+++ b/src/parser/transformer.cpp
@@ -21,7 +21,7 @@ Transformer::~Transformer() {
 }
 
 void Transformer::Clear() {
-	SetParamCount(0);
+	ClearParameters();
 	pivot_entries.clear();
 }
 
@@ -59,7 +59,7 @@ unique_ptr<SQLStatement> Transformer::TransformStatement(duckdb_libpgquery::PGNo
 	auto result = TransformStatementInternal(stmt);
 	if (!named_param_map.empty()) {
 		// Avoid overriding a previous move with nothing
-		result->named_param_map = std::move(named_param_map);
+		result->named_param_map = named_param_map;
 	}
 	return result;
 }
@@ -90,6 +90,12 @@ void Transformer::SetParamCount(idx_t new_count) {
 	root.prepared_statement_parameter_index = new_count;
 }
 
+void Transformer::ClearParameters() {
+	auto &root = RootTransformer();
+	root.prepared_statement_parameter_index = 0;
+	root.named_param_map.clear();
+}
+
 static void ParamTypeCheck(PreparedParamType last_type, PreparedParamType new_type) {
 	// Mixing positional/auto-increment and named parameters is not supported
 	if (last_type == PreparedParamType::INVALID) {


Test patch:
diff --git a/tools/pythonpkg/tests/fast/api/test_duckdb_query.py b/tools/pythonpkg/tests/fast/api/test_duckdb_query.py
index 1ced34502903..43f36603f00b 100644
--- a/tools/pythonpkg/tests/fast/api/test_duckdb_query.py
+++ b/tools/pythonpkg/tests/fast/api/test_duckdb_query.py
@@ -41,6 +41,24 @@ def test_duckdb_query_empty_result(self):
         res = con.query('show tables').fetchall()
         assert res == []
 
+    def test_parametrized_explain(self, duckdb_cursor):
+        query = """
+            EXPLAIN ANALYZE
+            SELECT
+                *
+            FROM (VALUES
+                (1, 'John'),
+                (2, 'Jane'),
+                (3, 'Doe')
+            ) my_table(id, name)
+            WHERE name = ?
+        """
+        params = ("John",)
+        duckdb_cursor.execute(query, params)
+
+        results = duckdb_cursor.fetchall()
+        assert 'EXPLAIN_ANALYZE' in results[0][1]
+
     def test_named_param(self):
         con = duckdb.connect()