Skip to content

feat(querybuilder): add typed composition and dialect-safe functions - #17

Merged
relsunkaev merged 13 commits into
mainfrom
feat/querybuilder-capabilities
Jul 30, 2026
Merged

feat(querybuilder): add typed composition and dialect-safe functions#17
relsunkaev merged 13 commits into
mainfrom
feat/querybuilder-capabilities

Conversation

@relsunkaev

Copy link
Copy Markdown
Owner

This lands the querybuilder capability work, including production wiring, documentation, and live PostgreSQL/MySQL/SQLite coverage.

Why

The builder was missing a composable path for dynamic queries, typed SQL fragments, reusable execution, and richer result contracts.

The original function surface also treated several dialect-dependent behaviors as portable. That made result types and runtime semantics look more consistent than the databases actually are.

New Features

Composition and execution:

  • typed SQL fragments preserve dependencies, result types, and parameter ordering
  • conditional selections and predicates compose through the existing pipeable query workflow
  • prepared handles reuse rendered plans
  • cardinality helpers stay pipeable instead of expanding the executor method surface
  • EXPLAIN and mutation metadata retain dialect-owned behavior

Functions and types:

  • PostgreSQL, MySQL, and SQLite own native aggregate, modulo, round, case-conversion, clock, and explicit-frame semantics
  • root functions keep only the cross-dialect contract
  • count and ranking values decode as BigIntString
  • numeric operations validate database types rather than only TypeScript runtimes
  • portable type witnesses move from Query.type to Type

Breaking Changes

  • use execute(...).pipe(Executor.atMostOne/exactlyOne/nonEmpty) instead of executor cardinality methods
  • Query.keyset is removed pending a reviewed pagination contract
  • use Type.<kind>() instead of Query.type.<kind>()
  • root Function no longer exports native sum, avg, case-conversion, clock, modulo, or round helpers
  • explicit aggregate and value window frames use dialect Function modules
  • count and ranking results are branded bigint strings

Risks & Testing Needed

Risk: consumer compile failures from the intentional API removals
→ the changelog and README show the replacement imports and pipeable calls

Risk: numeric result mismatches across drivers
→ live tests assert exact, approximate, integer, zero-divisor, aggregate, and rounding behavior on all three engines

Reversed/removed features:

  • the first compound keyset API was removed rather than shipped without an explicit cursor and ordering contract
  • nonportable standard function exports were moved to dialect modules

Build status: pass locally

Test Plan

bun run test:types
bun test
bun run test:integration

Results:

  • 550 unit and behavior tests passed
  • 70 live integration tests passed
  • package build and packed consumer smoke passed

Rollback

git revert <merge-commit>

This restores the previous public surface; consumers should not adopt the new APIs until the release is confirmed.

Add typed SQL fragments, numeric operators, runtime predicate composition, portable analytic windows, and forward keyset pagination through the shared AST and renderers.
Expose cardinality helpers and mutation metadata, cache rendered plans for reusable handles, and execute dialect-aware EXPLAIN statements without taking ownership from the underlying SQL driver.
Exercise the public types and rendered SQL, then prove result metadata, reusable execution, EXPLAIN, analytics, and keyset pagination against the supported database engines.
Document typed fragments, dynamic filters, analytics, keyset pagination, result contracts, prepared reuse, and EXPLAIN, with regenerated README type checks.
Replace executor cardinality methods with pipeable transforms, remove the compound keyset modifier, and keep dialect-specific functions and frames out of the standard namespace.\n\nBREAKING CHANGE: use execute(...).pipe(Executor.atMostOne/exactlyOne/nonEmpty); Query.keyset and non-portable standard function exports are removed.
Type PostgreSQL numeric bind literals, preserve textual bind ordering in window expressions, and emit MySQL lag/lead offsets as validated integer literals. Verify one standard function matrix live on all three dialects.
Document pipeable result helpers, explicit pagination composition, portable window behavior, and dialect-owned arbitrary calls. Record the durable API rules and regenerate README type checks.
Expose portable database type constructors through Type and remove the nested Query.type namespace.

BREAKING CHANGE: use Type.<kind>() instead of Query.type.<kind>() for portable type witnesses.
Add PostgreSQL, MySQL, and SQLite round and modulo APIs with dialect-owned input, result, nullability, and runtime semantics. Update portable type witness call sites to the root Type module.
Move dialect-dependent aggregate, case conversion, clock, and explicit-frame behavior behind dialect modules. Tighten the remaining standard function types and runtime result contracts.

BREAKING CHANGE: Root Function no longer exports sum, avg, lower, upper, or clock helpers; explicit frames must use dialect Function helpers; count and ranking functions now decode as BigIntString.
@relsunkaev
relsunkaev merged commit db32df2 into main Jul 30, 2026
5 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.

1 participant