Skip to content

Add CH.sql templates inside expressions, and Db.sql join/raw/empty - #17

Merged
Makisuo merged 4 commits into
mainfrom
feat/sql-template
Oct 4, 2026
Merged

Makisuo merged 4 commits into
mainfrom
feat/sql-template

Conversation

@Makisuo

@Makisuo Makisuo commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

The sql item from design/gap-review.md (Maple has ~163 sql uses). Stacked on #16.

CH.from(Keys)
	.select(($) => ({ txid: CH.sql(PG.text)`pg_current_xact_id()::xid::text`, next: CH.sql(PG.int8)`${$.uses} + ${1}` }))
	.where(($) => [CH.sql.cond`${$.meta} @> ${CH.param.string("filter")}::jsonb`])
// … WHERE "keys"."meta" @> $1::jsonb

What changes

  • Three forms. CH.sql(type) gives a typed Expr (it decodes when selected), CH.sql`…` an untyped one (costs the row schema, like untypedExpr), and CH.sql.cond a Condition. They work in select, where, ON, and an UPDATE's SET.
  • How each interpolated value renders:
    • Columns, expressions and nested templates render as their SQL.
    • param.* stays a placeholder, bound on Postgres.
    • A builder query becomes a subquery compiled with the outer query, with its tenant scope counted.
    • A string, number, boolean, Date, DateTime.Utc or null becomes the dialect's escaped literal.
    • An array or object has no literal without its SQL type, so it fails the compile with a message pointing at param.of.
  • Helpers. sql.ident quotes plain names, sql.raw splices text you control, and sql.join renders and joins values.
  • Db.sql gains join (one bound value per item), raw and empty.

A deliberate limit

A plain value inside CH.sql is an escaped literal, not a bound parameter. That matches how the builder treats values everywhere else; params are what bind. Values and params can't leak into SQL either way.

Testing

  • bun run typecheck and bun run test: 558 unit tests and the doc checks pass. The live ClickHouse suite passes on 26.2 and 26.8.
  • New src/ch/sql-template.test.ts covers both dialects, params, raw/ident/join, subquery tenant scope, every failure, and use in UPDATE.
  • PGlite: a jsonb @> filter with a bound param, ->> and a typed xid cast. New Db.sql render tests.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

CH.sql(type)`...` is a typed expression, CH.sql`...` an untyped one and
CH.sql.cond`...` a condition, usable anywhere the builder takes one:
select, where, ON, an UPDATE's SET. Interpolated columns, expressions
and nested templates render as SQL; params stay placeholders (bound on
Postgres); a builder query becomes a subquery compiled with the outer
query, its tenant scope counted; a plain value becomes the dialect's
escaped literal. Arrays and objects have no literal without their SQL
type, so they fail the compile and point at param.of. sql.ident quotes
plain names, sql.raw splices text, sql.join renders and joins values.

Db.sql gains join (one bound value per item), raw and empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 14 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d4ca1f4e-46f7-45ce-870c-6af2296f7dd2
📥 Commits

Reviewing files that changed from the base of the PR and between 13eea13 and fa5554b.

📒 Files selected for processing (12)
  • .gitignore
  • CHANGELOG.md
  • design/gap-review.md
  • docs/database.md
  • docs/extending.md
  • docs/reference.md
  • src/ch/index.ts
  • src/ch/sql-template.test.ts
  • src/ch/sql-template.ts
  • src/database.ts
  • src/database/database.test.ts
  • src/database/sql.ts
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Makisuo and others added 3 commits October 4, 2026 15:57
From review:
- A template condition was spliced bare, so CH.sql.cond`a OR b` in a
  where list became `a OR b AND org = $1`, reading every tenant while the
  query still reported single-tenant. Templates now render in
  parentheses (expressions too); sql.join stays bare to fit IN (...).
- sql.raw / sql.ident, and Db.sql's templates, identifiers and raw text,
  were recognised by a string _tag, which an object parsed from request
  JSON could forge to splice SQL (pre-existing for Db.sql templates).
  They are now recognised by identity through a private WeakSet; a forged
  object is a value: bound on Postgres, an escaped literal or an error on
  ClickHouse.
- A negative number after `-` wrote `10--1`, commenting out the rest of
  the line; negatives, and params ClickHouse inlines, are parenthesized.
- An empty sql.join / Db.sql.join fails instead of writing `IN ()`; an
  invalid Date is a typed failure; DateTime is detected with
  DateTime.isDateTime; a unionAll gets a message pointing at fromUnion.
- Stop tracking the node_modules symlink the worktree committed, and
  ignore node_modules whether it is a directory or a link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the follow-up review: Db.sql`SELECT 10-${-1}` wrote `10--1` on
ClickHouse, which inlines values, commenting out the rest of the line.
Postgres binds values, so it was unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Makisuo
Makisuo changed the base branch from feat/predicates-distinct-locking to main October 4, 2026 14:02
@Makisuo
Makisuo merged commit 90b846b into main Oct 4, 2026
3 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