Skip to content

cursor.description becomes None after fetchall() #1322

@mistercrunch

Description

@mistercrunch

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:

  1. The description is correctly populated after execute()
  2. Native DuckDB cursors preserve description after fetchall()
  3. 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

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions