Skip to content

schema-aware streams and zero-row wire results - #19

Merged
jeffreyaven merged 5 commits into
mainfrom
show-dependencies-wire-results
Oct 7, 2026
Merged

jeffreyaven merged 5 commits into
mainfrom
show-dependencies-wire-results

Conversation

@jeffreyaven

@jeffreyaven jeffreyaven commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Expose non-consuming ISQLResultStream.GetColumns() and public ColumnProvider.
  • Require exactly one provider in NewChannelSQLResultStream(provider ColumnProvider). ISQLResult already satisfies the interface. Getters delegate on demand; construction does not read the provider or consume results.
  • Define stream schema before iteration in simple and extended execution. Reuse a successful portal Describe's columns and formats without duplicate RowDescription; nil Describe metadata still permits Execute to emit stream schema.
  • Complete structured zero-row/zero-column results normally, without EmptyQueryResponse.
  • Remove obsolete DataWriter.Empty(), its implementation, ErrDataWritten, and the unused row counter. Actual empty statements are handled independently by the simple/extended protocol handlers.
  • Add getter delegation/nonconsumption, schema-before-read, zero-row/zero-column, multi-batch, Describe/Execute format, and actual empty-statement protocol regression coverage.

Supports stackql/stackql#766 and accompanies stackql/stackql#807.

Scope

Schema awareness and zero-row result sets only. row.go and legacy row_test.go match origin/main exactly. No NULL encoding changes or binary-NULL completeness claims.

No StackQL repository files were modified. No merge or tag.

Validation

Go 1.26.6, Windows AMD64, CGO_ENABLED=0:

  • go test -timeout 120s ./... - passed.
  • go vet ./... - passed.
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0 run --new-from-rev=origin/main --max-issues-per-linter 10 - passed; 0 new issues.
  • go test -timeout 120s -count=10 -run 'Test(EmptyStatementsWireProtocol|ZeroColumnResultCompletesNormally|ChannelSQLResultStream.*|SimpleQueryStreamSchema|ExtendedQueryStreamSchema|ZeroRowResultRetainsColumnMetadata)$' . .\pkg\sqldata - passed, ten repeated runs.
  • git diff --check - passed.
  • git diff origin/main --exit-code -- row.go row_test.go - passed; encoder and legacy row tests unchanged.
  • CI on 83031391569ab2583e584d697a17aa7ad277fa10: tests passed, lint passed, GitGuardian passed. No CI failures.

Empty-statement tests cover empty strings, whitespace, and semicolon-only statements in simple callback, simple backend, and extended Execute paths: EmptyQueryResponse followed by readiness, without result headers, CommandComplete, or backend execution. A zero-column provider returning an empty slice instead produces RowDescription with zero columns and normal CommandComplete.

Migration

Commit: 83031391569ab2583e584d697a17aa7ad277fa10

Pseudo-version: v0.1.2-beta01.0.20261007005816-83031391569a

  • Add side-effect-free GetColumns() []ISQLColumn to custom result streams.
  • Replace every no-argument or variadic channel constructor call with NewChannelSQLResultStream(result) or another single ColumnProvider handle. No legacy no-argument construction is supported.
  • For zero-column results, supply a provider returning a non-nil empty slice, e.g. a result constructed with []ISQLColumn{}.
  • Remove DataWriter.Empty calls and ErrDataWritten references. Structured results use Define/Row/Complete; actual empty-statement protocol handling belongs to the server handlers.
  • Repin and revalidate the StackQL consumer independently; prior consumer evidence is not validation of this final companion API.

Review

4201815090: provider handle rather than captured column array; delegated on demand.
4201855729: obsolete DataWriter.Empty API removed.
4201899781: constructor is unary, not variadic/optional; all companion callers migrated.
4192485322: zero rows are not an empty statement. The prior Complete branch emitted RowDescription, EmptyQueryResponse, CommandComplete, clearing client metadata. Structured zero-row results now complete normally; actual empty statements retain their independent EmptyQueryResponse path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread writer.go
return ErrClosedWriter
}

if writer.written == 0 && writer.columns != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks perfectly valid, why are you getting rid of it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The former condition conflated a structured zero-row result with an empty statement: after RowDescription it emitted EmptyQueryResponse and then CommandComplete, clearing psycopg2 column metadata. A zero-row result must instead retain its header and complete normally. Current head 8303139 also removes the obsolete Empty writer API as requested in 4201855729, while actual empty statements emit EmptyQueryResponse through dedicated simple/extended handlers. Zero-row metadata, zero-column completion, and actual empty-statement wire tests pass, as do full tests/vet, pinned changed-code lint, and CI.

Expose non-consuming stream schema, define it before iteration, and reuse described portal columns and result formats. Remove the previous NULL encoding changes and related tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jeffreyaven jeffreyaven changed the title fix zero-row metadata and SQL NULL encoding schema-aware streams and zero-row wire results Oct 6, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pkg/sqldata/sqldata.go Outdated
}

func NewChannelSQLResultStream() ISQLResultStream {
func NewChannelSQLResultStream(columns ...[]ISQLColumn) ISQLResultStream {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor here should not receive the column array itself but rather a handle to it; some interface that supplies the column array on demand. Just gives us more flexibility

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in 1b2ac74. NewChannelSQLResultStream now accepts optional ColumnProvider handles and delegates GetColumns on demand; ISQLResult already satisfies the interface. Zero-argument callers remain supported. Added repeated delegated/non-consuming getter coverage and updated tests/docs. Full tests, vet, pinned changed-code lint, and CI passed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This latest does not look right. I cannot imagine a valid reason to use variadic providers. A result with no columns should have a provider returning an empty array. In a complex result set with many columns, the abstraction remains unary,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed follow-up 4201899781 in 8303139: NewChannelSQLResultStream(provider ColumnProvider) is now strictly unary, with no variadic or no-argument compatibility path. Every companion call site is migrated. Zero-column tests pass an ISQLResult provider returning a non-nil empty slice; repeated GetColumns calls delegate without construction-time lookup or consuming the stream. Full tests/vet, pinned changed-code lint (0 issues), ten repeated targeted runs, and CI passed. Consumer migration is documented in the PR.

Delegate schema lookup on demand without consuming results, preserving zero-argument callers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread writer.go
}

if writer.written == 0 && writer.columns != nil {
err := writer.Empty()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok now that eager column defnition is held to be valid on all paths, the prior, defective Empty() method is obviated. Please ratify this idea and, if agreed, then remove it from the interface and implementation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and implemented in 8303139: removed DataWriter.Empty from interface and implementation, along with ErrDataWritten and the obsolete row counter. Actual empty-statement responses now live independently in simple/extended protocol handlers. TestEmptyStatementsWireProtocol checks EmptyQueryResponse followed by readiness for empty/whitespace/semicolon-only queries without invoking the backend; TestZeroColumnResultCompletesNormally checks structured zero columns receive RowDescription + CommandComplete. Full tests/vet, pinned changed-code lint (0 issues), ten repeated targeted runs, and CI all passed.

Keep empty-statement responses in protocol handlers, separate from schema-aware structured results. Migrate channel callers and cover zero-column and empty-statement protocol behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pkg/sqldata/sqldata.go
@jeffreyaven jeffreyaven self-assigned this Oct 7, 2026
@jeffreyaven
jeffreyaven merged commit 8f98baf into main Oct 7, 2026
3 checks passed
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.

2 participants