Effect ClickHouse

API reference

Everything on this page is exported from the root entry point (@maple-dev/effect-clickhouse) unless marked otherwise.

Naming conventions

Some ClickHouse functions collide with JavaScript reserved words or globals. The source defines those with a trailing underscore, and the root barrel drops it: min_, max_, any_, toString_, length_, left_, extract_, least_, greatest_, position_, lower_, round_, path_ and domain_ are all exported from the root under their bare names.

One exception, because it cannot be anything else:

Root barrel nameAlso on /expr asNote
if_if_if is a reserved word
in_ / notIn—Expr methods; in is reserved

Importing the kitchen-sink namespace (import * as CH from "@maple-dev/effect-clickhouse/expr") gives you the raw underscored names uniformly, which some codebases prefer for exactly this reason.

What's only on a subpath

The root barrel is curated. These are exported by the package but not from it:

SymbolSubpath
toFragment — value → SqlFragment, for hand-rolled function wrappers/expr
raw, str, ident, int, join, as_, lazy, when, compile, escapeClickHouseString/sql
SqlQuery, compileQuery/sql
ClickHouseStatement, parseStatement, renderStatement, withSettings, withFormat/sql
ClickHouseStatementFromString, splitTerminalClauses, maskLiteralsAndComments/sql
defineSuite, query, caseFromCompiled, runSuite, compareRuns, compareBudgets/benchmark
Suite, RunOutput, BenchmarkError and benchmark contracts/benchmark
makeHttpClient, makeHttpTransport, httpConfigFromEnv/benchmark/http
runCli/benchmark/cli

Every column-type constructor and every expression helper is on the root as well as on its subpath. See Running a query for what the /sql statement helpers are for. See Benchmarking for the complete benchmark API, command workflow, result verification, and JSON protocol.

Note /sql exports a compile (fragment → string) distinct from the root compile (query → CompiledQuery), and a when distinct from the root when (optional conditions).


Entry points

Query construction

ExportSignature
table(name, columns, options?) => Table
custom(sql, schema, literalSchema?) => CHType
from(table, alias?) => CHQuery
fromQuery(query, alias) => CHQuery
fromUnion(union, alias) => CHQuery
unionAll(...queries) => CHUnionQuery

CHQuery methods

MethodNotes
select(...names) / select(fn)Required before compiling
where(fn)Returns Array<Condition | undefined>; AND-joined
groupBy(...outputKeys)Takes select aliases, not column names
having(fn)Post-aggregation filter; input accessor or dynamicColumn aliases
orderBy(...[col, dir])Tuples, not two strings
limit(n) / offset(n)Rounded before emission
format(fmt)"JSON" | "JSONEachRow"
innerJoin / leftJoin / crossJoin(table, alias, on?)
innerJoinQuery / leftJoinQuery / crossJoinQuery(query, alias, on?)
withCTE(name, query) / withCTE(name, sql, options?)Typed query derives scope; SQL form can declare options.tenantScope
route("ingest")Metadata only
crossTenant()Forces tenantScope: "cross-tenant"

CHUnionQuery offers only orderBy, limit, offset, format.

Compilation

ExportSignature
compile(query, params, options?) => Effect<CompiledQuery<Output>, QueryBuilderError>
compileUnsafeThe same, returning CompiledQuery<Output> and throwing instead
compileUnion(union, params, options?) => Effect<CompiledQuery<Output>, QueryBuilderError>
compileUnionUnsafeThe same, throwing instead
rawCompiledQuery({ sql, tenantScope, reason, justification, rowSchema?, route? }) => CompiledQuery

Params

param.string(name), param.int(name), param.float(name), param.bool(name), param.dateTime(name), param.dateTimeString(name), param.dateTimeSeconds(name), and param.of(type, name) for any column type. Each checks the value it is handed at compile time; see Params and compilation.


Expressions

ExportPurpose
lit(value)Literal Expr from a string or number
rawExpr(sql, type)Unescaped Expr from SQL text, with a declared type
untypedExpr<T>(sql)Unescaped Expr with no type — costs the row schema
rawCond(sql)Unescaped Condition from SQL text
when(value, fn)Condition | undefined; skips undefined/null/false
whenTrue(flag, fn)Boolean-gated variant
inList(expr, values)expr IN ('a', 'b')
inExprList(expr, exprs)Same for expression lists
notInList(expr, values)expr NOT IN ('a', 'b')
not(condition)NOT (…)
dynamicColumn(name, t?)An Expr from a runtime column name — a GROUP BY alias
exists(q)EXISTS (…) from a query or pre-compiled SQL
inSubquery(expr, q)expr IN (…) from a query or pre-compiled SQL
notInSubquery(expr, q)expr NOT IN (…); note the NULL semantics
outerRef<T>(name)Reference an outer column in a correlated subquery

Expr<T> methods: eq, neq, gt, gte, lt, lte, in_, notIn, like, notLike, ilike (string-only), and add, sub, mul, div, mod (number-only, no parentheses). div and mod decode as number | null — ClickHouse sends inf/nan as JSON null — except by a numeric literal of magnitude ≥ 1 (Quotient<L, R>), which keeps the dividend's nullability; use ifNull(ifNotFinite(expr, 0), lit(0)) for a guaranteed number otherwise.

Spliced sub-SELECTs

For SQL the builder has no syntax for — an inner query's text inside an aggregate or a tuple comparison. The inner query is compiled by the outer compile, so its params resolve from the outer set and its failures land in the outer error channel. See Joins and subqueries.

ExportPurpose
subqueryExpr(q, type, wrap?)Inner SQL as an Expr of a declared type
untypedSubqueryExpr<T>(q, wrap?)Same with no type — costs the row schema
subqueryCond(q, wrap)Inner SQL as a Condition

wrap receives the inner SQL and returns the text to emit. It defaults to wrapping the SQL in parentheses, which is the plain "this value is a sub-SELECT" case.

Condition methods: and, or (both parenthesise; both drop the tenant marker).

ColumnRef adds .get(key) for Map columns; the result decodes as the map's value type.

Extensibility

ExportPurpose
defineFn<Args, R>(name, result)Declare a standard fn(args…) returning Expr<R>
defineUntypedFn<Args, R>(name)Same, for a result with no type to declare
defineCondFn<Args>(name)Same, returning Condition
sameAs(i)Result rule: decodes as argument i
firstTyped()Result rule: the first argument that has a type
firstTypedNonNull()Same, minus | null — coalesce, ifNull
elementOf(i)Result rule: one element of argument i's array
arrayOfArg(i)Result rule: an array of argument i
compileFnCall<R>(name, ...args)Variadic/generic wrapper (untyped result)
compileTypedFnCall<R>(name, schema,)Same, with the result codec
compileFnCallCond(name, ...args)Same, returning Condition
makeExpr<T>(fragment, schema)Build an Expr from a fragment and its codec
makeUntypedExpr<T>(fragment)Same with no codec — costs the row schema
makeCond(fragment)Build a Condition from a fragment
schemaOf(expr)An expression's codec, or undefined
schemaOfAny(...exprs)The first codec among several
elementSchema(expr)The element codec of an array expression
withoutNull(schema)A codec minus its null arm, or undefined
paramPlaceholder(kind, name)The __PARAM_…__ text, for handwritten fragments

ClickHouse functions

Aggregate

count(), countIf(cond), avg(e), sum(e), min(e), max(e), any(e), uniq(e), sumIf(e, cond), avgIf(e, cond), minIf(e, cond), maxIf(e, cond), anyIf(e, cond), groupUniqArray(e), groupUniqArrayIf(e, cond), groupUniqArrayArray(e), uniqIf(e, cond), uniqExact(e), argMin(value, order), argMax(value, order), argMaxMerge(e), quantile(q)(e) (curried), windowFunnel(window, mode?)(ts, ...conds) and sequenceMatch(pattern)(ts, ...conds) (both curried; WindowFunnelMode is the mode union).

min/max return Expr<NonNullable<T>>; groupUniqArray returns Expr<ReadonlyArray<T>>.

String

toString(e), length(e), lower(e), hex(e), match(e, pattern), matchCond(e, pattern) → Condition, domain(url), path(url), cutQueryString(url), position(haystack, needle), positionCaseInsensitive(a, b), left(e, n), extract(e, pattern), replaceOne(haystack, pattern, replacement), concat(...exprs), hasToken(haystack, token), hasAllTokens(haystack, tokens).

hasToken and hasAllTokens return Condition.

Numeric

toFloat64(e), toFloat64OrZero(e), toUInt16OrZero(e), toUInt64(e), toInt64(e), intDiv(a, b), round(e, decimals?), least(...exprs), greatest(...exprs), cityHash64(...exprs).

Date/time

toStartOfInterval(col, seconds), toStartOfHour(col), toUnixTimestamp(col), toUnixTimestamp64Nano(col), intervalSub(col, seconds), intervalAdd(col, seconds), formatDateTime(col, format), toDateTime(col), toStartOfMinute(col), toHour(col).

Conditional

if_(cond, then, else), multiIf([[cond, value], …], fallback), coalesce(...exprs), nullIf(expr, value), ifNotFinite(expr, fallback) (expr unless it is nan/inf — the SQL-side guard for division; preserves SQL NULL). nullIf returns Expr<T | null>. avg, avgIf, and quantile return Expr<number | null> for empty input.

Array

arrayOf(...exprs), arrayStringConcat(arr, sep), arrayFilter(fn, arr), arrayJoin(arr), arraySort(arr), arrayReverseSort(arr), arrayDistinct(arr), arrayPushFront(arr, value), arrayElement(arr, index), has(arr, value) → Condition.

Map

mapContains(map, key) → Condition, mapGet(map, key), mapKeys(map), mapValues(map), mapLiteral(...[key, expr]). Prefer $.Column.get(key) for a declared Map column.

JSON

toJSONString(e).

Window

over(expr, spec), windowSpec({ partitionBy?, orderBy?, frame? }), rowsBetween(start, end), lagInFrame(expr, offset, defaultValue) (all three arguments required), and the frame bounds currentRow, unboundedPreceding, unboundedFollowing, preceding(n), following(n).

CH.over(
	CH.lagInFrame($.DurationMs, 1, 0),
	CH.windowSpec({
		partitionBy: [$.Name],
		orderBy: [[$.Timestamp, "asc"]],
		frame: CH.rowsBetween(CH.unboundedPreceding, CH.currentRow),
	}),
)

Types: WindowSpec, CompiledWindowSpec, WindowFrameBound, WindowRowsFrame, WindowOrderDirection.


Types

Column-type constructors — string, bool, uint8, uint16, uint32, uint64, int32, int64, float64, dateTime, dateTime64, dateTimeString, dateTime64String, map, array, nullable, aggregateState(fn, ...args), custom(sql, schema, literalSchema?), and untyped(sql) for a wire value passed through unvalidated. See Tables and column types.

Type descriptors — CHType, CHString, CHBool, CHUInt8, CHUInt16, CHUInt32, CHUInt64, CHInt32, CHInt64, CHFloat64, CHDateTime, CHDateTime64, CHDateTimeString, CHDateTime64String, CHMap, CHArray, CHNullable.

Inference — InferTS (the decoded type of a column), InferEncoded (its wire type), InferOutput, InferQueryOutput, InferUnionOutput, OutputToColumnDefs, NullableColumnDefs, ColumnDefs.

Everything else — Table, TableOptions, Expr, ColumnRef, Condition, Comparable (what a value of a type may be compared against), MapValueOf, Subquery, ParamMarker, ParamKind, CHQuery, CHUnionQuery, ColumnAccessor, JoinedColumnAccessor, JoinOnCallback, CompiledQuery, CompiledQueryInput, CompiledQueryRowSchema, RowSchemaMismatch, TenantScope, FnResult, WindowFunnelMode, WindowSpec, WindowRowsFrame, WindowFrameBound, WindowOrderDirection, CompiledWindowSpec.

Errors

These errors are Effect Schema.TaggedError classes. Expected failures can be caught by their full namespaced tag; QueryBuilderDefect remains a defect rather than a typed failure.

QueryBuilderError

Tag "@maple-dev/effect-clickhouse/QueryBuilderError". Raised while compiling, and surfaced in compile's error channel (thrown by compileUnsafe).

codeCause
UnresolvedParamA param the params bag has no value for
InvalidLiteralA param value, or a comparison operand, the column's codec rejects
InvalidArgumentsArguments a function cannot use — an empty condition list, a bad pattern

QueryBuilderDefect

Tag "@maple-dev/effect-clickhouse/QueryBuilderDefect". A DSL misuse no runtime value can cause — a query with no select(), an orderBy entry that is not a tuple, a bad param name, a comparison called on a param marker. Always a defect: compile maps only QueryBuilderError into the error channel. See Failures and defects.

CompiledQueryEncodeError

Tag "@maple-dev/effect-clickhouse/CompiledQueryEncodeError". Fails the encodeRows Effect when a decoded row cannot be written back to its wire shape. Fields: message, rowIndex, cause.

CompiledQueryDecodeError

Tag "@maple-dev/effect-clickhouse/CompiledQueryDecodeError". Fails the decodeRows / decodeFirstRow Effect. Fields: message, rowIndex, cause.

On this page