Repository navigation
feat(query): a pipeable, data-last Query API next to the fluent builder - #102
Conversation
|
Warning Review limit reachedYou'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 42 minutes. View limit details
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. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds Query.groupBy, having, offset, format, distinct, distinctOn, the four row locks, route, crossTenant, innerJoinQuery / leftJoinQuery / crossJoinQuery and withCTE, as the issue asks for every builder method. The subquery joins take a query first, so they tell data-first from data-last by arity. A test checks that every fluent method has a Query function, and that piped, data-first and fluent forms compile to the same SQL (or the same refusal) on ClickHouse, Postgres, SQLite and D1. Records the sqlite and d1 snapshots of the live pipe-data-last case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge-readiness pass (maintainer side):
Verified locally against ClickHouse 26.8: |
A query has only fluent methods. In an Effect codebase you cannot compose reusable query parts with
pipe, as you do withEffect,StreamandSchema.Closes #65.
Part of #52.
Changes
CHQueryis nowPipeable(effectPipeable):query.pipe(f, g)isg(f(query)).Querynamespace on/clickhouseand/postgres(fromsrc/core.ts, filesrc/ch/query-pipe.ts):select,where,orderBy,limit,innerJoin,leftJoin,crossJoin, plusfrom,fromQueryandfromUnionas start points.Function.dualthat calls the fluent method, so the two styles share one implementation. Each function has a data-last form (query.pipe(Query.where(fn))) and a data-first form (Query.where(query, fn)).$in a data-last callback gets its type from the query thatpipecarries. A wrong column, anorderBykey that is not selected, a negativelimitand an alias that is already in use are type errors at that step.src/ch/query-pipe.test.tscompares the compiled SQL of the data-last, data-first and fluent forms for each function and for a full chain (ClickHouse and Postgres SQL, parameters, tenant scope).src/ch/query-pipe.test-d.tschecks that the result types are the same as the fluent types, that params propagate, and the type errors above. A new core casepipe-data-lastruns a piped chain live on ClickHouse (both join modes) and on Postgres.docs/queries.md(backed by a test indocs-examples.test.ts), aQuerytable indocs/reference.md, and one changeset (minor).Left out
CHQuerymethods:groupBy,having,offset,format,distinct,distinctOn, the row locks,route,crossTenant,innerJoinQuery/leftJoinQuery/crossJoinQueryandwithCTE. They follow the same pattern. I kept this PR small, so that you can review the shape first. The issue's "done when" asks for every method. I can add the rest in this PR or in a follow-up.Pipeableand data-last forms forCHUnionQuery,insertInto,updateanddeleteFrom.Query.select(...).pipe(Query.from(t)). Here a query starts atfrom, because the column types come from the table. Aselectbeforefromwould need a different builder state.byTenantfor every table with anOrgIdcolumn). Comparison operands on a generic column type do not resolve, in the fluent builder too. A reusable part for one table works: type its$withColumnAccessor<typeof Table.columns>(see the docs).pipe, asEffect.mapdoes. If you type a callback's$with fewer columns than the query has, the query type narrows to those columns.Validation
All commands ran on this branch, rebased on
upstream/main(c5fa679), with a live ClickHouse 26.8.2.7 (EFFECT_ORM_CLICKHOUSE_URL/_USER/_PASSWORDset, so no suite skipped):bun run build: ok.bun run typecheck: exit 0.bunx vitest run --testTimeout=60000 --hookTimeout=60000: 61 files passed, 1046 tests passed, 0 skipped.node scripts/check-doc-citations.mjs: ok (25 files).node scripts/check-exports-documented.mjs: ok (288/clickhouse, 206/postgresexports).node scripts/check-doc-examples.mjs: ok (21 files typechecked).bun scripts/check-package.ts: tarball imports, types, compilation and codecs passed; CommonJS consumer ok.Upstream CI for a fork PR waits until a maintainer approves the run.
Need help on this PR? Tag
@codesmith-botwith what you need.