Split query plan and row output into separate sinks - #95
Merged
Conversation
--plan-output writes a rows-less ResultSet envelope and strips stats.queryPlan from the primary document. Default stdout output is unchanged. Files publish via temp+rename; a missing plan or failed sink does not replay SQL.
Two review findings on the split-output head 9c7ebdd: - sinks.Finish errors (temp-file rename of the primary or plan artifact) were returned unwrapped from runCLI. After a read-write transaction committed or a partitioned DML finished, a bare "failed to publish plan output" invites replaying the statement. runCLI now wraps a Finish error with the committed-DML wording whenever the statement itself succeeded in a committing mode. - The eager JSON/YAML path with --discard-results materialized every row before skipping jq. Materialize now drops row values whenever the run will not emit rows (--redact-rows or --discard-results), matching the CSV and lazy paths which already drain. Adds unit tests for the mode classification, the materialize decision, and the wrapped publish failure.
apstndb
added a commit
that referenced
this pull request
Sep 8, 2026
Add --reauth=off|auto (env EXECSPANSQL_REAUTH, default off) so Google Workspace reauthentication failures on user Application Default Credentials are classified before any Spanner RPC and, with explicit consent, repaired by one interactive `gcloud auth application-default login`. Classification is structural: the typed token error (auth.Error body or oauth2.RetrieveError) must carry error=invalid_grant with error_subtype invalid_rapt or rapt_required; message-only matches receive the google-auth hint text but never trigger a login. With `off` client construction is unchanged and only the hint is added. With `auto` a preflight token fetch runs before client creation, gcloud is launched once (stdout to stderr, fixed argv), the well-known ADC file is re-read and verified, a second token fetch must succeed, and the client is created with option.WithAuthCredentials. Applicability requires user ADC at the well-known path, no GOOGLE_APPLICATION_CREDENTIALS or CLOUDSDK_CONFIG, no emulator, a terminal on stdin and stderr, gcloud on PATH, and no injected client options. SQL and parameters are frozen before the login; --timeout starts after it; the first SIGINT cancels and the second terminates. DML and in-flight statements are never replayed. Verification: go build, vet, GOTOOLCHAIN=go1.25.13 golangci-lint (0 issues), full go test ./... with the Colima emulator on a038e5d; classifier, orchestration, applicability, fake-gcloud, and transport tests added. Independent review: grok GO at r1 (570b6c4), r2 (c9932d8, params frozen before preflight), and r3 (a038e5d, resolution against main after #95); reports ~/tmp/apstndb/execspansql/2026-09-08-feature-notes/merge-reviews/feature-merge-93-r{1,2,3}.md. Known limits: Windows gcloud.cmd launch untested in CI; mid-run RAPT expiry after the preflight is hint-only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--output/-o,--plan-output,--plan-format, and--discard-resultsso one query can emit rows and a plan artifact to different destinations.--plan-outputenables split mode: the primary document is the current ResultSet withstats.queryPlanremoved (CSV stays rows-only); the plan artifact is a rows-less ResultSet envelope (metadataplus fullstats).CLI contract
--output PATH(-o)---plan-output PATH--plan-format json|yaml--formatwhen that is json/yaml, else json--discard-results--plan-output.Destination spellings:
-is stdout;/dev/stdoutand/dev/stderrare mapped in-process (Windows-safe, included in collision checks); anything else is a regular file.Validation before a client is created:
--plan-outputrequires--query-mode=PLAN,PROFILE, orWITH_PLAN_AND_STATS.--plan-formatand--discard-resultsrequire--plan-output.--plan-outputis incompatible with--try-partition-queryand--enable-partitioned-dml.os.SameFilewhen the target exists), or an output that aliases--sql-file/--param-file/--filter-fileis rejected.A compatible mode that still returns nil/empty
planNodesis an artifact-generation error after execution; the statement is not retried. Split mode disables jq early stop and drains remaining rows without retaining them.--redact-rowsis unchanged and independent of--discard-results. After committed DML, an output/render failure is reported as not a rollback and SQL is not replayed.What stays unchanged
--experimental-trace-stdout) still writes to stderr.Tests run
go build ./...go test ./params/... ./jqresult/... ./resultset/...golangci-lint runGOTOOLCHAIN=go1.25.13 golangci-lint rungo run . --helpDOCKER_HOST=unix://$HOME/.colima/default/docker.sock TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock go test ./...Known limitations
text,dot,svg, ...) are out of scope;--plan-formatis json/yaml only.queryStatsfor PROFILE but noQueryPlan. Emulator split runs therefore hit the documented missing-plan error after publishing CSV. Happy-path plan envelopes (non-emptyplanNodes, zero-row/PLAN metadata, discard-results, lazy drain) are covered by the existing fake ExecuteStreamingSql server.--overwrite/--no-clobber).