Skip to content

feat(query,write): HTTP wire adapter + query/write commands + 4 output formats (PR2)#1

Merged
xe-nvdk merged 1 commit into
mainfrom
feat/pr2-query-write
May 31, 2026
Merged

feat(query,write): HTTP wire adapter + query/write commands + 4 output formats (PR2)#1
xe-nvdk merged 1 commit into
mainfrom
feat/pr2-query-write

Conversation

@xe-nvdk

@xe-nvdk xe-nvdk commented May 31, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds internal/client/ — typed HTTP adapter for /api/v1/query, /api/v1/query/arrow, and /api/v1/write/line-protocol.
  • Wires arcctl query and arcctl write commands via cobra.
  • Renders query results in table (default), JSON, CSV, and Arrow IPC (binary on stdout, server execution-time read from the response trailer).
  • arcctl query accepts SQL as positional arg, -f file.sql, or stdin pipe.
  • arcctl write reads stdin or -f file.lp, streams the body (large-file friendly), validates --precision ns|us|ms|s client-side.
  • Centralised buildClient() enforces the connection-precedence rules from PR1 and emits TLS-skip warnings only on https:// (no-op + suppressed on http://).

Test plan

  • go test -race -count=1 ./... — 17 new tests across internal/client/ and internal/output/
  • gofmt -l . && go vet ./... clean
  • End-to-end smoke against arc serve: config createwrite via stdin LP → write -f with --precision msquery -o table/json/csv/arrow all verified. Arrow IPC produced a valid 456-byte stream from real DuckDB and the Arc-Execution-Time-Ms trailer round-tripped (server execution 1ms showed on stderr).
  • Error paths smoked: bad SQL → 500 surfaced, bad token → 401 surfaced, half-set ad-hoc (--endpoint only) → caught client-side, bad --precision → caught client-side, --insecure warning fires on https:// and is suppressed on http://.
  • Empty-result rendering: (0 rows) printed instead of blank output.

Internal review

Matrix + single deep reviewer per .claude/CLAUDE.md. Findings addressed pre-commit:

  • H1 Arrow trailer test reshaped to use http.TrailerPrefix (the production pattern)
  • H2 Mid-stream interruption now prints a clear arrow: stream interrupted after N bytes line to stderr alongside the error
  • M1 --timeout 0s now errors instead of producing an instantly-cancelled context
  • M2 TLS warnings route through cmd.ErrOrStderr() for test capture
  • M4 RenderQueryResult nil-guard
  • Style Close() doc no longer overclaims idempotency; isPipe comment clarifies the bytes.Buffer-in-tests rationale
  • Deferred error-shape evolution to json.RawMessage (tracked for when Arc's error contract changes)

Companion docs PR

User-facing docs land in docs.basekick.net at docs/cli/{index,connections,query,write}.md per the per-PR-docs rule.

🤖 Generated with Claude Code

…formats

PR2 of the phased arcctl build-out. Adds `internal/client/` (the
typed HTTP adapter for /api/v1/query, /api/v1/query/arrow,
/api/v1/write/line-protocol), the `arcctl query` and `arcctl write`
cobra commands, and a query-result renderer covering table, JSON,
CSV, and Arrow IPC output.

Highlights
- `arcctl query "SELECT ..."` with -f/stdin/positional SQL input
- 4 output formats: table (default), json, csv, arrow (binary IPC on
  stdout; server execution-time read from Arc's response trailer)
- `arcctl write` reads stdin or -f; --precision ns|us|ms|s validated
  client-side; body is streamed, never buffered (large file friendly)
- buildClient() centralises the connection-precedence resolution
  shared by both commands; --insecure / insecure_tls only warn on
  https:// (no-op + suppressed on http)
- Bounded response body reads (64 MiB cap on query JSON), bounded
  error-body reads (64 KiB) to keep a misbehaving proxy from OOMing
  the CLI
- Empty result on -o table now prints "(0 rows)" instead of nothing
- --timeout 0s now errors instead of producing an already-cancelled
  context

Verification
- Unit tests: 9 client tests (httptest) + 8 renderer tests, race-clean
- End-to-end smoke against arc serve: write stdin LP, write -f, query
  -o table/json/csv/arrow (Arrow IPC trailer round-tripped; 456-byte
  stream from real DuckDB validated)
- Internal review (matrix + single deep reviewer): 2 High addressed
  (trailer test shape, mid-stream interruption UX), 3 Medium
  addressed (timeout guard, stderr routing through cmd.ErrOrStderr,
  nil QueryResult guard), 2 Style addressed (Close() docstring
  overclaim, isPipe comment clarity); 1 Medium deferred (error-shape
  evolution to json.RawMessage; tracked for when Arc's error
  contract changes)

Companion docs PR lands in docs.basekick.net (docs/cli/{index,
connections,query,write}.md).
@xe-nvdk

xe-nvdk commented May 31, 2026

Copy link
Copy Markdown
Member Author

@gemini-code-assist review please

@xe-nvdk xe-nvdk merged commit fa5ede0 into main May 31, 2026
2 checks passed
xe-nvdk pushed a commit that referenced this pull request Jun 1, 2026
)

Two findings from Gemini's review on #3,
both Medium.

#1 — `--skip-rows` accepts negative values silently
A negative value passes through to `ImportCSV`, then gets dropped by
the `opts.SkipRows > 0` gate in the client. User typed `--skip-rows -5`,
nothing happens, no error. Now validated in the command's RunE before
any client call. Regression test driven through cobra so the wired flag
path is covered end-to-end; mutation-verified by disabling the guard
(downstream "no database" check fires instead).

#2 — `mw.Close()` on error paths writes trailing boundary into broken pipe
In the multipart goroutine, the error paths used to call `mw.Close()`
*before* `pw.CloseWithError(err)`. `multipart.Writer.Close()` writes
the trailing boundary `--<boundary>--\r\n` into the pipe — but the
preceding content is already known-bad (CreateFormFile failed, or
io.Copy errored mid-file). Result: a multipart payload that's
syntactically valid but semantically truncated, confusing for a reader
attempting to make sense of partial data. Now: error paths skip
`mw.Close()` entirely and signal the error on the pipe; happy path
still closes the writer normally to emit the trailing boundary.
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.

1 participant