Skip to content

feat(sql): GROUP BY, aggregates, DISTINCT, LIKE, IN (SQLR-3)#97

Merged
joaoh82 merged 1 commit into
mainfrom
feat/sqlr-3-group-by-aggregates
May 6, 2026
Merged

feat(sql): GROUP BY, aggregates, DISTINCT, LIKE, IN (SQLR-3)#97
joaoh82 merged 1 commit into
mainfrom
feat/sqlr-3-group-by-aggregates

Conversation

@joaoh82

@joaoh82 joaoh82 commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the analytical-SQL gap that was sitting under "Possible extras":

  • LIKE / NOT LIKE / ILIKE with %, _, \-escape; case-insensitive ASCII to match SQLite's default
  • IN (list) / NOT IN (list); subquery form rejected with NotImplemented
  • SELECT DISTINCT (single + multi-column; NULL == NULL for dedupe)
  • GROUP BY on bare columns (acts like implicit DISTINCT when no aggregates)
  • Aggregates: COUNT(*), COUNT([DISTINCT] col), SUM, AVG, MIN, MAX
  • Column aliases (COUNT(*) AS n) and ORDER BY by alias or aggregate display form
  • Friendly error when an aggregate name lands in WHERE

SUM stays INTEGER until a REAL input or i64 overflow promotes it once to REAL (SQLite-style). AVG always returns REAL (or NULL on empty / all-NULL groups).

What's not in this PR (filed as follow-ups)

  • HAVING
  • LIKE … ESCAPE '<char>'
  • IN (subquery)
  • DISTINCT on SUM/AVG/MIN/MAX
  • GROUP BY on expressions
  • Window functions, BETWEEN, GROUP_CONCAT, GLOB / REGEXP

Implementation map

  • New module src/sql/agg.rsAggState, DistinctKey, like_match. Pure-functional; no Database / Table coupling.
  • src/sql/parser/select.rsProjection becomes Items(Vec<ProjectionItem>), with ProjectionKind::{Column, Aggregate} and an optional alias. SelectQuery gains distinct: bool and group_by: Vec<String>. Parser validates that bare projection columns appear in GROUP BY.
  • src/sql/executor.rs — adds Expr::Like, Expr::ILike, Expr::InList, Expr::InSubquery arms in eval_expr. New aggregation path in execute_select_rows that bypasses the rowid-shape optimizers (HNSW / FTS / bounded-heap) when grouping. DISTINCT post-pass and aggregate-aware ORDER BY by alias / display form.

Docs

  • README.md feature table updated; "Possible extras" pruned.
  • docs/supported-sql.md — full SELECT semantics rewrite (DISTINCT / GROUP BY / aggregates / LIKE / IN sections, with semantics callouts).
  • docs/sql-engine.md — new "Aggregation phase" section under the executor doc.

Verification

  • cargo build --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --all-targets — clean
  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --all-targets — no new warnings (only pre-existing ones unrelated to this PR)
  • cargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs — engine tests went from 411 → 434 (+22 SQLR-3 integration tests + 12 new agg.rs unit tests; some pre-existing tests collapsed into new helpers)
  • REPL smoke: CREATE, INSERT, LIKE, IN, DISTINCT, GROUP BY with all 5 aggregates + COUNT(DISTINCT) + ORDER BY n DESC — all render correctly via prettytable.

Test plan

  • CI passes (build, fmt, clippy, tests)
  • Local REPL smoke still green after rebase to main
  • Sanity-check the README "Not yet supported" line still reads cleanly after the trim

🤖 Generated with Claude Code

Closes the analytical-SQL gap that was sitting under "Possible extras":
- LIKE / NOT LIKE / ILIKE with %, _, \-escape; case-insensitive ASCII
  to match SQLite's default
- IN (list) / NOT IN (list); subquery form rejected with NotImplemented
- SELECT DISTINCT (single + multi-column; NULL == NULL for dedupe)
- GROUP BY on bare columns (implicit DISTINCT when no aggregates)
- Aggregates: COUNT(*), COUNT([DISTINCT] col), SUM, AVG, MIN, MAX
- Column aliases (COUNT(*) AS n) and ORDER BY by alias or display form
- Friendly error when an aggregate name lands in WHERE

SUM stays Integer until a Real input or i64 overflow promotes it once
to Real (SQLite-style); AVG always returns Real (NULL on empty).
HAVING, LIKE...ESCAPE, IN (subquery), DISTINCT on SUM/AVG/MIN/MAX, and
GROUP BY on expressions are explicit follow-ups.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joaoh82 joaoh82 merged commit bc34877 into main May 6, 2026
16 checks passed
@joaoh82 joaoh82 deleted the feat/sqlr-3-group-by-aggregates branch May 6, 2026 04:56
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