Skip to content

Fix stream_blocks dropping column info for empty result sets - #233

Open
jenish-25 wants to merge 1 commit into
suharev7:async-awaitfrom
jenish-25:fix/stream-blocks-empty-table-columns
Open

jenish-25 wants to merge 1 commit into
suharev7:async-awaitfrom
jenish-25:fix/stream-blocks-empty-table-columns

Conversation

@jenish-25

Copy link
Copy Markdown

Fixes #222.

Problem

For a SELECT over an empty table, QueryResult::stream_blocks() yields no blocks at all, so callers lose all column/schema information. fetch_all() is unaffected.

Root cause

ClickHouse precedes a query's data blocks with a single schema-only header block (columns present, num_rows == 0). stream_blocks() deliberately skips this first block (via block_index > 1) because for non-empty results the following data blocks already carry the full schema, so the empty header would just be noise.

When the result set is empty, however, the header is the only block the server sends before EOF. Skipping it leaves the stream empty and the caller has no way to learn the columns/types. (fetch_all() avoids this because it drives the internal stream with skip_first_block = false.)

Fix

Buffer the header block instead of discarding it:

  • If a real data block arrives later, the buffered header is dropped — behaviour is unchanged for non-empty results (only data blocks are streamed).
  • If EOF is reached without any data block, the buffered header is emitted so its column info is preserved — empty results now stream exactly one block with the columns and zero rows.

Testing

Added test_empty_select_stream_blocks, which streams SELECT 1 AS a WHERE 1 <> 1 and asserts a single 1-column, 0-row block is produced. Confirmed it fails before this change and passes after.

Verified against a local clickhouse-server; the new test and the existing streaming/empty-select tests pass, with no regressions introduced by this change.

ClickHouse precedes the data blocks of a SELECT with a single schema-only
header block (columns present, zero rows). `stream_blocks()` skips this
first block because for non-empty results the subsequent data blocks
already carry the full column schema.

However, when the result set is empty (e.g. a SELECT over an empty table)
the header is the *only* block sent, so skipping it made the stream yield
nothing and callers lost all column information. `fetch_all()` was
unaffected because it does not skip the first block.

Buffer the header block instead of discarding it and flush it on EOF when
no data block ever arrived. Empty results now expose their columns while
non-empty results keep their existing behaviour.

Fixes suharev7#222
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

streaming in QueryResult lost column infos when the target table is empty

1 participant