Skip to content

feat: Add Type-Safe SELECT/WHERE/JOIN Query Builder - #2

Merged
ThomasHartDev merged 4 commits into
mainfrom
thomas/feat/query-builder
Sep 4, 2026
Merged

ThomasHartDev merged 4 commits into
mainfrom
thomas/feat/query-builder

Conversation

@ThomasHartDev

Copy link
Copy Markdown
Owner

This adds a small typed query builder: defineTable, from/select/where/join/orderBy/limit/offset, and compile() returning { sql, params }. Postgres uses $n, SQLite uses ?. Self-joins go through alias(), and a second where() ANDs onto the first because the builder is immutable.

Empty IN () is invalid SQL so it becomes 1 = 0. Equality against null or undefined throws and points you at isNull()/isNotNull(). SQLite OFFSET without LIMIT compiles as LIMIT -1 OFFSET ? because SQLite does not accept a bare OFFSET clause. Postgres still allows OFFSET alone. SQLite bind params encode JS booleans as 1/0 and Date as ISO strings because node:sqlite rejects those JS types. Postgres keeps true/false and Date objects. Left joins do not yet widen selected columns to T | null. compile() does not yet check that selected columns belong to tables in FROM/JOIN.

The sqlite prepare proof uses node:sqlite, which needs Node 22. CI and package.json engines are pinned to Node 22. execSqlite stays a hard require; it does not skip when the builtin is missing.

Tests cover injection strings, dialect placeholders, nested boolean algebra, sqlite boolean/Date binds, sqlite statement prepare, Node 22 CI/engines, and the inferred row type of a select shape.

Closes #1

Compile typed table definitions into parameterized SQL. Values bind
through $n or ?, identifiers are quoted, and NULL / empty IN follow
SQL's actual rules instead of string concatenation.
SQLite rejects a bare OFFSET clause. compile('sqlite') now inserts
LIMIT -1 when offset is set and limit is not, and the sqlite tests
prepare the statement so a syntax error fails. Postgres still allows
OFFSET alone. NOT predicates wrap as (NOT (inner)).
GitHub Actions was on Node 20, but the sqlite prepare test loads
node:sqlite, which landed in 22.5. Pin engines and CI to 22 so that
proof actually runs on the public workflow.
node:sqlite rejects JS booleans and binds Date as NULL. Convert true/false
to 1/0 and Date to ISO strings in Binder so eq, inList, like, and cmp share
the conversion. Postgres still receives the original JS values.
@ThomasHartDev
ThomasHartDev merged commit ec29709 into main Sep 4, 2026
1 check passed
@ThomasHartDev
ThomasHartDev deleted the thomas/feat/query-builder branch September 4, 2026 19:18
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.

Add a type-safe SELECT/WHERE/JOIN query builder

1 participant