Quarry

Debug a query

Trace an unexpected result from your installed version through types, SQL, and execution.

Check the installed version

If an example's method is missing, start with npm ls quarry or pnpm why quarry. These docs cover Quarry 0.10.0, including APIs absent from 0.9.1. Check release guidance before working around a missing method.

Inspect the SQL and bound values

TypeScript
const compiled = query.toSQL();
console.log(compiled.query);
console.log(compiled.params);

Check the table alias, predicate, grouping, ordering, and limits together. Parameters remain separate from the SQL text. Avoid logging sensitive parameter values in production logs.

You can run the compiled query directly through your existing client:

TypeScript
const result = await client.query({
  query: compiled.query,
  query_params: compiled.params,
  format: "JSONEachRow",
});
const rows = await result.json();

This checks the generated SQL independently of Quarry's execution helper. For a fair comparison, preserve the result settings required by your query, especially outer-join null behavior and JSON serialization. The runtime semantics guide explains those settings.

To investigate execution cost, send the same SQL and parameters through the client with an appropriate ClickHouse EXPLAIN prefix. Quarry does not currently provide an explain() helper. See the ClickHouse EXPLAIN reference.

Match the symptom to the cause

SymptomCheck
Optional filter has no effectAssign the new builder returned by where(); builders are immutable
Column is rejected by TypeScriptUse the active alias and select the column from the subquery that exposes it
An aggregate alias is rejected in GROUP BYGroup by the underlying expression; use output aliases for HAVING or ORDER BY
Counts are stringsUInt64 uses strings in ClickHouse JSON output
Unmatched LEFT JOIN values are zero or empty stringsOrdinary joins use ClickHouse defaults; leftJoinNullable() in 0.10.0 opts into nulls
Only one result is neededFirst-row helpers in 0.10.0 add LIMIT 1; on 0.9.1, call .limit(1) explicitly
A first-row query is still expensiveLIMIT restricts returned rows; sorting and aggregation can still process many input rows
An export uses too much memoryConsume stream() incrementally and await destination writes

Make parameter types explicit

Plain schemas do not provide runtime metadata for every ClickHouse type. Use param(...) when a value needs a specific placeholder type:

TypeScript
import { param } from "quarry";

const filtered = query.where("created_at", ">=", param("2026-08-01 00:00:00", "DateTime"));

For null checks, use whereNull() or whereNotNull(). To bind a nullable value explicitly, use param(null, "Nullable(String)"). Bare null predicates are rejected.

Report a reproducible problem

Include the Quarry version, a reduced schema, the builder chain, generated SQL and parameters, and the expected result. For a type error, include the TypeScript version and exact diagnostic. Remove private data before sharing.

Bring a query that demonstrates the issue. Use toAST() only if you need to inspect the builder's internal query representation as well.

On this page