Skip to content

Add the Layer 3 fluent query builder SDK - #55

Merged
wudidapaopao merged 7 commits into
chdb-io:mainfrom
ShawnChen-Sirius:feat/layer3-fluent-sdk-v2
Jun 25, 2026
Merged

wudidapaopao merged 7 commits into
chdb-io:mainfrom
ShawnChen-Sirius:feat/layer3-fluent-sdk-v2

Conversation

@ShawnChen-Sirius

Copy link
Copy Markdown
Contributor

Layer 3 — fluent, immutable query builder for chdb-node

This PR adds Layer 3, the Kysely-shaped fluent query builder, on top of the pluggable Connection introduced in #52 (now merged into main). Code: src/layer3/, tests: test/v3/layer3/. Spec: chdb_node_layer3_lld_en.md.

What's included

  • SQL compiler with server-side value binding — the compiler emits {pN:Type} placeholders plus raw values and calls Layer 1 queryBindAsync; Layer 1 owns value serialization (src/serialize.ts). No value ever enters the SQL string, identifiers are backtick-quoted. ChdbCompileError is the only net-new (build-time) error. Layer 1 null semantics are left exactly as they are on main.
  • Builders — SELECT (Kysely-shape verbs), INSERT, UPDATE (ALTER … UPDATE), DELETE (ALTER … DELETE), and INSERT…SELECT; terminals .compile / .execute / .executeTakeFirst[OrThrow]; json/arrow/raw format views; root factory (selectFrom / database / session).
  • ClickHouse dialect sugar — final / sample / prewhere / settings / format / limitBy; chTable table-function helpers (args bound); chFn function helper incl. parametric name(params)(args).
  • connect() federation — maps all 14 URL schemes to table functions with every arg bound; Connection.table()/.describe()/.databases()/.tables()/.snapshot(). Metadata wired for ClickHouse, Postgres, and MySQL; clickhouseSettings forwarded to chDB, remote-only options accepted-and-ignored (no throw).
  • Typed row inference — Database<DB> flows through to .execute(); chdb-gen-types CLI with runtime introspection plus static Drizzle/Prisma schema conversion.
  • Arrow C Data Interface input — registerArrowTable(name, columns) / chTable.arrowstream(name) register JS columnar buffers zero-copy via 4 new N-API methods in lib/chdb_node.cpp.
  • db.introspection — Kysely-instance-shaped getTables/getSchemas/getMetadata over system.databases/tables/columns.

Testing

  • 190 Layer 3 tests; full v3 suite green (364 pass / 4 skip locally), build:ts clean, native addon rebuilt.
  • Golden-SQL, injection, and adversarial suites for the builder; live federation tests against real local Postgres + MySQL (skip when servers are unreachable).

🤖 Generated with Claude Code

@ShawnChen-Sirius
ShawnChen-Sirius force-pushed the feat/layer3-fluent-sdk-v2 branch from 6b024c3 to 710a8ea Compare June 24, 2026 06:56
@ShawnChen-Sirius
ShawnChen-Sirius requested a review from Copilot June 24, 2026 07:31
@ShawnChen-Sirius

Copy link
Copy Markdown
Contributor Author

@chibugai, review this

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.

Pull request overview

This pull request introduces Layer 3, a fluent, immutable query builder on top of the existing Layer 1 runtime, along with a chdb-gen-types CLI for typed schema generation and an Arrow C Data Interface input path for zero-copy columnar ingestion.

Changes:

  • Adds Layer 3 builder surface (SELECT/INSERT/UPDATE/DELETE), compiler, execution terminals, and local-engine introspection.
  • Adds federation via connect() with URL-scheme → table-function mapping and metadata discovery helpers.
  • Adds schema codegen (runtime introspection + static Prisma/Drizzle parsing) and an Arrow table registration API backed by new N-API exports.

Reviewed changes

Copilot reviewed 45 out of 47 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/v3/layer3/types.test.ts Type-level assertions for CH type literal → TS type mapping and row inference.
test/v3/layer3/static-codegen.test.ts Tests static schema conversion (Prisma/Drizzle) and CLI output behavior.
test/v3/layer3/introspection.test.ts Exercises db.introspection against the embedded engine.
test/v3/layer3/functions.test.ts Tests function/table-function helpers compilation and one execution path.
test/v3/layer3/fixtures/prisma-schema.prisma Prisma fixture for static conversion tests.
test/v3/layer3/fixtures/drizzle-schema.ts Drizzle-shaped TS fixture for static conversion tests.
test/v3/layer3/execute.test.ts End-to-end execution tests for core builder behavior and types.
test/v3/layer3/dialect.test.ts Tests ClickHouse dialect sugar compilation + observable execution behavior.
test/v3/layer3/connect.test.ts Tests URL-scheme mapping, injection resistance, metadata SQL, and execution.
test/v3/layer3/compile.test.ts Golden-SQL compilation tests to enforce value-binding and quoting.
test/v3/layer3/codegen.test.ts Tests runtime introspection, deterministic emission, and CLI behavior.
test/v3/layer3/arrow-input.test.ts Tests Arrow input registration, refresh/close semantics, and validation.
src/layer3/types/infer.ts Defines schema → row inference and RowOf helper for builder typing.
src/layer3/types/ch-types.ts Implements CH type literal → TS type mapping via template literal types.
src/layer3/runtime.ts Lazy Layer 1 runtime accessor + minimal runtime interface for Layer 3.
src/layer3/introspection.ts Kysely-shaped introspector implementation over system.* tables.
src/layer3/index.ts Layer 3 public exports surface.
src/layer3/execute/terminal.ts Execution boundary forwarding compiled queries to Layer 1 bind APIs.
src/layer3/execute/format.ts Output-view planning (json/arrow/raw) and JSONEachRow parsing.
src/layer3/execute/arrow-input.ts JS API for registering Arrow columnar buffers as arrowstream() sources.
src/layer3/database.ts Root factories (selectFrom, database, session, connect) and context binding.
src/layer3/connect/url-scheme.ts URL-scheme → table-function mapping and validation for federation sources.
src/layer3/connect/snapshot.ts Builds INSERT … SELECT snapshot node for one-shot materialization.
src/layer3/connect/connect.ts Connection wrapper: table expression, describe, catalog listing, snapshot.
src/layer3/compiler/param.ts Placeholder naming + JS value → CH type inference for server-side binding.
src/layer3/compiler/nodes.ts AST node definitions for the builder/compiler boundary.
src/layer3/compiler/identifier.ts Identifier quoting + function name validation.
src/layer3/compiler/compile.ts SQL compiler producing { sql, parameters } and settings/format rendering.
src/layer3/codegen/introspect.ts Runtime DESCRIBE-based introspection over tables/files/expressions/URLs.
src/layer3/codegen/from-prisma.ts Static Prisma schema parsing → IntrospectedDatabase conversion.
src/layer3/codegen/from-drizzle.ts Static Drizzle schema parsing (TypeScript AST) → IntrospectedDatabase conversion.
src/layer3/codegen/emit.ts Deterministic TS emitter for schema interfaces (ClickHouse type literals).
src/layer3/codegen/cli.ts chdb gen-types CLI implementation and argument planning.
src/layer3/builder/update.ts UPDATE builder compiling to ClickHouse mutations.
src/layer3/builder/table-functions.ts chTable helpers for common table functions with bound arguments.
src/layer3/builder/select.ts Immutable SELECT builder + dialect sugar and execution terminals.
src/layer3/builder/insert.ts INSERT builder supporting row inserts and INSERT … SELECT.
src/layer3/builder/functions.ts chFn helper functions including parametric aggregates.
src/layer3/builder/expression.ts sql tag and expression helpers (ref, val, fn) for AST construction.
src/layer3/builder/delete.ts DELETE builder compiling to ClickHouse mutations.
src/errors.ts Adds ChdbCompileError for Layer 3 build/contract failures.
package.json Adds chdb-gen-types binary entrypoint.
lib/chdb_node.cpp Adds Arrow C Data Interface registration/unregistration N-API bindings.
index.mjs Exposes ChdbCompileError as a named ESM export.
index.js Exposes low-level Arrow registration/unregistration and re-exports Layer 3.
index.d.ts Re-exports generated Layer 3 TypeScript declarations from dist/layer3.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/layer3/codegen/from-prisma.ts Resolved
Comment thread src/layer3/execute/arrow-input.ts Resolved
Comment thread src/layer3/compiler/compile.ts Resolved
Comment thread src/errors.ts Resolved
Comment thread lib/chdb_node.cpp Resolved
Comment thread lib/chdb_node.cpp Resolved
Comment thread src/layer3/codegen/from-prisma.ts Resolved
Comment thread src/layer3/builder/select.ts Resolved
Comment thread src/layer3/execute/format.ts Resolved
Comment thread src/layer3/codegen/emit.ts Resolved
Comment thread src/layer3/compiler/compile.ts Resolved
Comment thread src/layer3/compiler/compile.ts Resolved
Comment thread src/layer3/codegen/cli.ts Resolved
Comment thread src/layer3/codegen/cli.ts Resolved
ShawnChen-Sirius and others added 5 commits June 24, 2026 09:07
Scaffold the Layer 3 package and ChdbCompileError, and implement the SQL compiler with server-side value binding ({pN:Type} placeholders + raw values via Layer 1 queryBindAsync; identifiers backtick-quoted). Add the SELECT/INSERT/UPDATE(ALTER..UPDATE)/DELETE(ALTER..DELETE)/INSERT..SELECT builders, expression helpers, terminals, and root factory; ClickHouse dialect sugar (final/sample/prewhere/settings/format/limitBy); chTable and chFn helpers; and golden-SQL, injection, and execution tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Map all 14 URL schemes to table functions with every arg bound, and expose Connection.table()/describe()/databases()/tables()/snapshot(). Wire metadata for ClickHouse, Postgres, and MySQL; forward clickhouseSettings to chDB and accept-and-ignore remote-only options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Infer row types from a typed Database<DB> through to .execute(). Add the chdb-gen-types CLI with runtime introspection helpers, plus static Drizzle and Prisma schema conversion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…face)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ShawnChen-Sirius
ShawnChen-Sirius force-pushed the feat/layer3-fluent-sdk-v2 branch from 710a8ea to f14908c Compare June 24, 2026 09:34
ShawnChen-Sirius and others added 2 commits June 25, 2026 00:34
Native (lib/chdb_node.cpp):
- erase a connection's Arrow-table registry entries on every close path
  (release_session_conn, the default-conn handoff, hard_close_all) so pinned JS
  buffers aren't leaked and a reused connection address can't collide with a
  stale key.
- reject negative length/nullCount and replace the rootLength == -1 sentinel
  with a bool flag so a 0-row column is handled correctly.

Builder / compiler:
- SETTINGS string values escape ' by SQL-standard doubling ('') instead of
  backslash, matching src/connection/chdb-connection.ts.
- exhaustiveness guards on the expr() and compileQuery() switches.
- chTable s3/postgresql/mysql/iceberg/deltaLake reject a half-supplied
  credential pair instead of silently dropping both.
- executeTakeFirst[OrThrow] narrow options to the row view so format:'arrow'/raw
  can't yield a malformed rows[0].

Codegen / introspection:
- stripComments no longer truncates // inside quoted strings (e.g. URLs);
  parsePrismaSchema/modelToColumns use null-prototype maps (no __proto__
  pollution).
- emitDatabase validates the interface name against IDENT_RE.
- gen-types CLI errors on duplicate --table and wraps the output write in
  try/catch with a clean exit code.
- introspection isNullable unwraps LowCardinality(Nullable(...)).

Execute:
- parseRows wraps JSON.parse to surface a typed ChdbInternalError (with the
  offending line) instead of a bare SyntaxError.
- registerArrowTable validates the nulls mask length.

Adds test/v3/layer3/review-fixes.test.ts covering these.

CI (fold-in of the earlier "Fix Layer 3 CI" commit):
- cleanroom failed in `npm pack` (TS5055 "would overwrite input file"): the pack
  step re-ran build:ts after `npm run build` had populated dist, and the second
  tsc pass reached dist/*.d.ts as inputs through the root index.d.ts barrel and
  refused to overwrite them. build:ts now `rm -rf dist` first so each pass starts
  clean.
- the test job failed with `ArrowRegisterColumns is not a function`: the loader
  prefers the published @chdb/lib-<platform> subpackage over a local build, and
  that prebuilt binary predates the Layer 3 Arrow C Data Interface methods. The
  job now drops the installed subpackage after building so loadNative() falls
  through to the freshly built build/Release/chdb_node.node.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
registerArrowTable pins JS buffers via Napi::Reference until the table is
unregistered. A handle dropped without close() previously leaked those pins
for the connection's lifetime, so a long-lived session registering tables in
a loop grew memory monotonically.

Add a module-level FinalizationRegistry that best-effort unregisters a
collected handle. The held value carries only rt/conn/name and a shared
{ live } flag — never the handle or the encoded buffers, which would pin the
very things we want freed. close() cancels the finalizer (handle is its own
token) and shares the live flag, so an explicit close never races a redundant
unregister, and a second close is a no-op. This is non-deterministic by spec;
explicit close() (ideally try/finally) is still required.

Test via a leaked handle plus driven GC; --expose-gc is wired through vitest
execArgv so it runs by default and self-skips otherwise.

Also add AGENT.md documenting the Arrow input lifecycle (close/refresh/
single-pass/GC fallback), the one-connection-per-process constraint, and the
build/test split. A lazy .stream() terminal is recorded there as a follow-up
rather than shipped: Layer 1's streaming C ABI (query_conn_streaming_n) takes
no params, so a streaming terminal could not bind server-side without either a
footgun (throw on bound queries) or putting values into SQL text (violates the
no-values-in-SQL invariant). The clean fix is upstream; build .stream() once
against it later.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
@ShawnChen-Sirius
ShawnChen-Sirius force-pushed the feat/layer3-fluent-sdk-v2 branch from f14908c to 795440a Compare June 25, 2026 02:33
@wudidapaopao
wudidapaopao merged commit 08d0d0e into chdb-io:main Jun 25, 2026
17 checks passed
ShawnChen-Sirius added a commit that referenced this pull request Jul 7, 2026
…w integrations

Additive minor release on top of 3.1.0 (2026-06-24). No breaking changes;
existing 3.1.0 APIs continue to work unchanged. libchdb bits are unchanged
(chdb-core v26.5.1-rc.1 packaged as @chdb/lib-* 26.5.2 — no republish
needed, the subpackage-matrix job will idempotent-skip).

## What's new

### Layer 3 fluent query builder SDK (#55, #59)

First-class typed fluent query API — Kysely-/Drizzle-style:

    const db = connect({ session })
    const rows = await db
      .from('users')
      .where(r => r.age.gt(18))
      .select('id', 'name')
      .execute()

- `src/layer3/{builder,execute,compile,dialect,introspection,connect,types,codegen}/`
- `chdb-gen-types` CLI — generates row types from a schema (Drizzle /
  Prisma / raw SQL sources)
- `.stream()` terminal with server-side parameter binding
- Arrow C Data Interface support: register JS columnar data as
  `arrowstream()` tables
- 12 new test files under `test/v3/layer3/`

### Cross-language agent-tool contract + 2 framework adapters (#61, #66)

- `chdb/ai-sdk` — Vercel AI SDK tools (`generateText`/`streamText`)
- `chdb/mastra` — Mastra tools + `ChDBVector` (HNSW RAG store) + `ChDBStore`
- Both are thin wrappers over `ChDBTool.call()` from `integrations/agents/`,
  which implements the cross-language contract vendored from `chdb-io/chdb`
  (`chdb/agents/CONTRACT.md`, chdb-io/chdb#597).
- 7 canonical tools: `run_select_query` / `list_databases` / `list_tables` /
  `describe_table` / `get_sample_data` / `list_functions` / `attach_file`
- 5 pillars enforced: engine-level readonly=2 (P1), value-vs-identifier
  separation (P2), truncation flag (P3), error envelope for model
  self-correction (P4), resource/source controls (P5)
- Behavior verified against the shared `conformance/cases.jsonl` fixture

### `chdb/hypequery` DatabaseAdapter (#63, #65)

Lets `@hypequery/clickhouse` query builder run on embedded chDB instead
of a remote ClickHouse server. Uses hypequery's own `substituteParameters`
(imported, not copied) so rendered SQL stays byte-identical to hypequery's
built-in HTTP adapter.

### `chdb/connection` stabilization for @clickhouse/client 1.23.0

- `peerDependency: "@clickhouse/client: >=1.23.0"` (optional) — old client
  triggers a clear npm warning instead of a runtime TS error
- Parity runner switched to track `ClickHouse/clickhouse-js` release tag
  `client-1.23.0` (per the sync-policy table; #52 shipped the initial hook)
- Runner auto-detects upstream Vitest layout (pre-/post-clickhouse-js#931)
- Runner re-synced when clickhouse-js reorganized into a monorepo (#62)

### AI-discovery docs (#60)

- Top-level `AGENTS.md` + `llms-full.txt`
- Per-subpath `AGENTS.md` (`src/layer3/`, `src/connection/`, etc.)
- All packaged in `files` so agents crawling the installed tarball find them

## Fixes / infrastructure

- Bind `null` / `undefined` query params as SQL `\N` (contributor
  @dfrankland, #68) — matches `@clickhouse/client` behavior
- `close()`-mid-flight engine abort + ResultSet teardown race (#56)
- `ChDBVector` HNSW granularity / filter binding / dimension guard (#61)
- Release workflow: publish → `unverified` dist-tag → clean-install verify
  matrix → promote to `latest` (#64). A broken release now never reaches
  `latest` users.
- CI: cache deps + harden npm registry fetch against transient resets

## User-facing installation matrix

```
npm install chdb                                    # base + Layer 3 SDK
npm install chdb @clickhouse/client                 # + chdb/connection
npm install chdb @hypequery/clickhouse              # + chdb/hypequery
npm install chdb ai zod                             # + chdb/ai-sdk
npm install chdb @mastra/core zod                   # + chdb/mastra
```

Native libchdb: chdb-core v26.5.1-rc.1 (packaged as @chdb/lib-* 26.5.2),
unchanged from 3.1.0.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
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.

4 participants