Repository navigation
Releases: rasuvaeff/clickhouse-toolkit
Releases · rasuvaeff/clickhouse-toolkit
Release list
v1.12.0
SelectQuery(#45): the statement builder around the WHERE the toolkit has
always had.$qb->select(table)->final()->columns()->aggregate()->groupBy() ->orderBy()->limit()->build()returns aParameterizedQuery {sql, params}—
aggregates, GROUP BY, FINAL and a bound-parameter LIMIT/OFFSET without
concatenating a skeleton aroundWhereClause::$sql, which is where
identifier validation used to stop applying.- Projections come from closed sets with private constructors:
Aggregate(count,sum,min,max,avg,uniqExact, and the
conditionalcountIf/sumIf/uniqExactIfover aFilterInterface) and
Scalar(toString). There is no raw-expression factory, so a projection
cannot become an injection point; columns and aliases are
identifier-validated at construction. - A conditional aggregate's condition renders through the same filter visitor
as WHERE (allow-list, type map, bound parameters); a fully dropped
condition throws instead of silently becoming the unconditional aggregate. - Mandatory filters apply exactly as in
buildWhere(). - LIMIT/OFFSET are emitted as
{limit:UInt32}/{offset:UInt32}placeholders
— constant statement text, onenormalized_query_hashin the query log;
name collisions are renamed throughPlaceholderRemap. - ORDER BY accepts the allow-list plus everything the query itself projects
or groups by (aggregate aliases included). buildOrderBy()gained an optionalarray $alsoOrderable = []parameter
(behaviour unchanged when omitted);renderFilter()is a new internal
helper. ExistingbuildSelect()/buildCount()/buildDistinct()are
untouched. JOIN/subqueries stay a documented raw-SQL escape hatch.
- Projections come from closed sets with private constructors:
v1.11.1
- Allow
rasuvaeff/context^0.3, so the package installs next to context 0.3. No code changes: nothing used here changed in 0.3 (all additions).
v1.11.0
- Fixed:
ContextClickHouseClientrounded the remaining budget up to whole
seconds (1.2 s left became 2 s), and turned a configured
max_execution_time => 0(unlimited) into 1 second. The budget is now passed
in fractional seconds; a looser or unlimited configured value is replaced
by it (#41). - Fixed: a deadline passing between the check and reading the budget could
producemax_execution_time = 0, i.e. unlimited; the budget is now read
before the check. - Added:
ContextClickHouseClientsets
cancel_http_readonly_queries_on_client_close=1unless the caller set it, so
the server stops a read-only query when the HTTP client gives up (#42).
v1.10.1
- Allow
rasuvaeff/context^0.2 (^0.1 || ^0.2), so the package installs next torasuvaeff/context-http0.2. No code changes: the package does not useContext::onTerminate(), the only API change in context 0.2.
v1.10.0
- Added
ContextClickHouseClient, which checks arasuvaeff/contextdeadline
around every operation and maps its remaining budget to ClickHouse's native
max_execution_timesetting.
v1.9.0
Fixed
- Two
ClickHouseRawFilters that reused a parameter name — under oneAndX/OrX,
or one in the mandatory filter and one in the user filter — merged by name, so
the SQL kept both{name:Type}tokens while only the last value was bound; a
raw name equal to a builder key (p0, …) or to the keyset boundary (ck0, …)
lost the builder's value the same way. Parameters are now isolated at both
merge points (ClickHouseSqlFilterVisitorcomposites and
ClickHouseQueryBuilder::buildWhere()): the colliding name becomesname_0,
name_1, … and its token is rewritten, whichever sibling comes second. A
customClickHouseFilterVisitorkeeps merging its own composites — only the
builder-level merge is applied to it. Theck0/ck1names are no longer
reserved (#34).
v1.8.0
ClickHouseMigrationRunner::run()andstatus()now throw a
ClickHouseMigrationExceptionnaming the path when$migrationsPathis not a
directory, instead of treating it as an empty set of files.glob()returns an
empty list for a mistyped path exactly as it does for an empty directory, so a
wrong path — the one setting that cannot be verified locally, where it is
correct by definition — made a deploy report success with no table created; the
failure surfaced later, on the first query against a table that was never made.
Behaviour change: a runner pointed at a directory that does not exist yet now
fails instead of returning[](#31).clickhouse:migrations:migratereports recorded migrations whose file is gone
from disk.run()only walks the files, so it could not see them, and the two
commands contradicted each other:statusreported1 missingwheremigrate
reported an up-to-date schema. The command now reads the status once after
applying, warns naming those records, and reflects them in the summary — while
still exiting0, since applying what is pending is its job and a deleted file
is not a reason to fail a deploy.clickhouse:migrations:statusremains the
gate that exits1(#31).ClickHouseDataReader::withFilter(),withSort(),withLimit()and
withOffset()carry theTValuetemplate argument over to the returned
instance, andreadOne()is annotatedTValue|null. Psalm used to widen the
reader back to itsarray|objectbound behind anywithX(), so every
read()/readOne()after one lost its type and consumers had to re-annotate
at each call site — an annotation that could not catch a mapper returning a
different object type, both satisfying the bound. Docblocks only; no runtime
or signature change (#32).
v1.7.0
ClickHouseMigrationRunneraccepts$migrationsTable: the bookkeeping table
recording applied migrations is no longer hardcoded to_migrations. Adopting
the runner where a table of that name already exists with a different schema
used to fail inside the first read — before any migration file was read, so
the repair could not itself ship as a migration — and two applications sharing
one ClickHouse database collided on it. The name is interpolated into SQL, so
it is validated as a plain identifier; a db-qualified form is refused. Existing
installations keep_migrations(#29).
v1.6.0
ClickHouseMigrationRunneraccepts$placeholders:{{key}}tokens in a
migration file are replaced before the file is hashed and executed, so a
package can ship DDL whose table names the application configures instead of
hard-coding them. An unresolved{{…}}throws aClickHouseMigrationException
naming the file and the token, rather than sending it to the server.- The checksum covers the resolved SQL, not the raw file. That is what makes
the feature safe to adopt: a package switching its shipped DDL from a literal
name to a placeholder is invisible to installations on the default value —
their resolved text is byte-identical to what they applied. Changing a value
after a migration has been applied is still reported as a divergence. status()resolves placeholders identically torun(), so the two cannot
disagree about whether a file has diverged.