Skip to content

Add LIMIT support to interactive SELECT queries - #30

Open
donatj wants to merge 7 commits into
masterfrom
fix/query-limits
Open

donatj wants to merge 7 commits into
masterfrom
fix/query-limits

Conversation

@donatj

@donatj donatj commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Large dumps need small, streaming query results. A limited query should return to the prompt after the requested rows. It should not keep scanning the rest of the data block or retain a large queue of decoded data.

This adds LIMIT count and LIMIT offset, count to user queries. Both values must be non-negative integers. Offsets apply across all INSERT blocks for a table. A zero count reads no row data. The README documents the supported grammar.

The branch includes the merged architecture guide from #31. Query execution now follows its boundaries:

  • ExecuteQuery lives in the library. It streams rows through a callback. The command owns output files and flushing.
  • The row parser runs synchronously. It shares the existing Pike-style state functions. Limit counters stay local to execution.
  • Data-section scans use a 64-token queue instead of a 2,000,000-token queue. The executor stops its lexer worker and waits for it before returning.
  • Read, parse, write, and flush errors reach the caller. Query workers no longer exit the process.
  • Integration tests cover real dump indexing, cached indexes, query parsing, execution, and output. An interactive process test keeps standard input open to catch completion regressions.

Semicolon-terminated queries run without a second semicolon or end of input.

Validation:

  • go test ./... -timeout 30s
  • go test -race ./... -timeout 60s
  • go test ./cmd/sqlread -run '^Test(QueryStopsReading|InteractiveQueries)$' -count=10 -timeout 60s
  • go build ./...
  • go vet ./...
  • go test ./cmd/sqlread -run '^$' -bench '^BenchmarkExecQuery$' -benchmem -benchtime=300ms -count=2

On a synthetic 20,001-row INSERT, executor allocations for LIMIT 10 fell from about 64 MB to 3.6 KB per call. Full-scan allocations fell from about 64.8 MB to 0.84 MB. Full scans were slightly faster in these runs. These are allocation measurements on an Apple M1 Max, not peak process memory. They exclude indexing and user-query parsing.

The architecture guide still identifies full-scan buffers, index growth, cache loading, and large individual values as areas for further work. This change does not claim to resolve those broader limits.

@donatj
donatj marked this pull request as ready for review October 11, 2026 10:41
Copilot AI balanced review requested due to automatic review settings October 11, 2026 10:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Offset-form limits currently return before reading any rows.

1 open finding
What changed in this PR

Adds MySQL-style LIMIT support to interactive SELECT queries and early termination of INSERT parsing.

Changes:

  • Parses count and offset/count LIMIT forms.
  • Stops lexing once enough rows are produced.
  • Documents supported query grammar and adds parser tests.
File Description
scanner.go Adds cancellable lexing and LIMIT token.
interpreter.go Lexes LIMIT clauses.
query.go Models and parses query limits.
cmd/​sqlread/​query.go Applies limits during execution.
detail.go Supports stopping row parsing.
query_test.go Tests LIMIT parsing and inclusion.
detail_test.go Tests parser cancellation.
lexitemtype_string.go Updates generated token strings.
README.md Documents grammar and LIMIT usage.
Files not reviewed (1)
  • lexitemtype_string.go: Generated file

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cmd/sqlread/query.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation consistently handles parsing, cross-block limits, cancellation, documentation, and relevant regressions.

0 open findings

1 resolved since last review
Files not reviewed (1)
  • lexitemtype_string.go: Generated file

🧠 Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

OUTFILE destinations are neither truncated nor closed, causing stale limited results and descriptor leaks.

2 open findings
Files not reviewed (1)
  • lexitemtype_string.go: Generated file

🧠 Review effort: Balanced

Comment thread execute.go
Comment on lines +8 to +10
// ExecuteQuery reads a SELECT's rows from the dump and passes them to writeRow.
// It stops after the requested limit or the first error. The caller owns the
// output destination and must flush it. Outfile is handled by the caller.
Comment thread query.go
Comment on lines +266 to +267
if d.Type == TIntpLimit {
return q.parseSelectLimitBuilder(qry)
@donatj
donatj changed the base branch from master to fix/stream-query-execution October 11, 2026 17:13
@donatj
donatj changed the base branch from fix/stream-query-execution to master October 11, 2026 17:28
@donatj donatj closed this Oct 11, 2026
@donatj donatj reopened this Oct 11, 2026
@donatj donatj closed this Oct 11, 2026
@donatj donatj reopened this Oct 11, 2026
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