From 2d6e8e618289dde0ec85839d30701e1f4503f2e6 Mon Sep 17 00:00:00 2001 From: DevOps Team Date: Wed, 11 Feb 2026 22:00:07 +0700 Subject: [PATCH] Fix tuple parameter handling for SQLAlchemy 2.0 pooled queries Fixes 'List of argument must consist only of dictionaries' error when running parallel queries with ODBC-style ? placeholders. The issue occurs because SQLAlchemy 2.0's text() expects named parameters (dict) but the code was passing positional parameters (tuples). Solution: - Detect tuple parameters in _execute_query_pooled - Convert tuples to dict with keys p1, p2, p3... - Replace ? placeholders with :p1, :p2... in the query string - Pass converted dict to conn.execute() This maintains backward compatibility with single-connection mode which uses pyodbc and handles tuple parameters natively. Affected queries: - table_exists (2 params: schema, table) - get_columns (2 params: schema, table) - get_primary_keys (2 params: schema, table) --- src/drt/database/executor.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/drt/database/executor.py b/src/drt/database/executor.py index bfe7d8d..9b7ca94 100755 --- a/src/drt/database/executor.py +++ b/src/drt/database/executor.py @@ -75,6 +75,24 @@ class QueryExecutor: raise ValueError("Only SELECT queries are allowed (READ ONLY)") try: + # Convert tuple params to dict for SQLAlchemy 2.0 compatibility + if isinstance(params, tuple): + # Map positional params to p1, p2, p3... + params_dict = {f"p{i}": val for i, val in enumerate(params, 1)} + # Replace ? placeholders with :p1, :p2... by iterating + query_parts = [] + last_pos = 0 + param_index = 1 + for char_pos, char in enumerate(query): + if char == '?': + query_parts.append(query[last_pos:char_pos]) + query_parts.append(f':p{param_index}') + last_pos = char_pos + 1 + param_index += 1 + query_parts.append(query[last_pos:]) + query = ''.join(query_parts) + params = params_dict + with self._engine.connect() as conn: result = conn.execute(text(query), params or {}) df = pd.DataFrame(result.all(), columns=result.keys())