What happened?
I've encountered an issue with duckdb-engine where cursor.description becomes None after calling fetchall(), which breaks compatibility with tools that rely on cursor metadata being available after data retrieval.
Context
This was discovered while working on Apache Superset integration (https://github.com/apache/superset) where we're migrating example datasets to use DuckDB. Superset's result processing relies on cursor.description being available after fetchall() to properly handle column names and types.
Expected Behavior
According to the Python DB-API 2.0 specification, cursor.description should remain available after fetchall() until the cursor executes a new statement. This is the standard behavior across all other database drivers (PostgreSQL, MySQL, SQLite, etc.).
Actual Behavior
With duckdb-engine, cursor.description becomes None immediately after calling fetchall(), even though:
- The description is correctly populated after execute()
- Native DuckDB cursors preserve description after fetchall()
- The SQLAlchemy result object maintains column information via result.keys()
Minimal Reproduction
from sqlalchemy import create_engine, text
engine = create_engine('duckdb:///test.db')
with engine.connect() as conn:
result = conn.execute(text('SELECT 1 as col1, 2 as col2'))
print("Before fetchall:", result.cursor.description)
# Output: [('col1', 'NUMBER', None, None, None, None, None), ('col2', 'NUMBER', None, None, None, None, None)]
rows = result.fetchall()
print("After fetchall:", result.cursor.description)
# Output: None (should still be the column info)
Impact
This breaks any downstream tool that needs cursor metadata after data retrieval, including:
- Apache Superset result processing
- Data analysis tools that inspect column types
- ORM frameworks that rely on cursor description
Environment
- duckdb-engine: 0.12.1 (also reproduced in 0.17.0)
- SQLAlchemy: 1.4+
- DuckDB: Latest
Workaround
We've implemented a workaround in Superset by capturing cursor.description before fetchall() and restoring it afterward, but this shouldn't be necessary.
Related Issues
This appears related to #1087 which describes cursor state corruption in multi-query scenarios. Both issues suggest cursor state management problems in the CursorWrapper implementation.
Happy to help debug this further or test any proposed fixes. Thanks for maintaining this important bridge between DuckDB and SQLAlchemy!
DuckDB Engine Version
duckdb-engine==0.17.0
DuckDB Version
duckdb==1.3.2
SQLAlchemy Version
flask-sqlalchemy==2.5.1
Relevant log output
Code of Conduct
What happened?
I've encountered an issue with duckdb-engine where
cursor.descriptionbecomesNoneafter callingfetchall(), which breaks compatibility with tools that rely on cursor metadata being available after data retrieval.Context
This was discovered while working on Apache Superset integration (https://github.com/apache/superset) where we're migrating example datasets to use DuckDB. Superset's result processing relies on cursor.description being available after fetchall() to properly handle column names and types.
Expected Behavior
According to the Python DB-API 2.0 specification, cursor.description should remain available after fetchall() until the cursor executes a new statement. This is the standard behavior across all other database drivers (PostgreSQL, MySQL, SQLite, etc.).
Actual Behavior
With duckdb-engine, cursor.description becomes None immediately after calling fetchall(), even though:
Minimal Reproduction
Impact
This breaks any downstream tool that needs cursor metadata after data retrieval, including:
Environment
Workaround
We've implemented a workaround in Superset by capturing cursor.description before fetchall() and restoring it afterward, but this shouldn't be necessary.
Related Issues
This appears related to #1087 which describes cursor state corruption in multi-query scenarios. Both issues suggest cursor state management problems in the CursorWrapper implementation.
Happy to help debug this further or test any proposed fixes. Thanks for maintaining this important bridge between DuckDB and SQLAlchemy!
DuckDB Engine Version
duckdb-engine==0.17.0
DuckDB Version
duckdb==1.3.2
SQLAlchemy Version
flask-sqlalchemy==2.5.1
Relevant log output
Code of Conduct