Fix ClickHouse LowCardinality + Nullable wrapping order#14
Conversation
ClickHouse rejects Nullable(LowCardinality(String)) with ILLEGAL_TYPE_OF_ARGUMENT. The valid form wraps LowCardinality outermost: LowCardinality(Nullable(String)). Apply Nullable before LowCardinality in both the FixedString and scalar type branches of the schema compiler, and update the docs accordingly.
Greptile SummaryFixes the
Confidence Score: 5/5Safe to merge — the change is a two-line reorder in each of two code paths, both covered by targeted tests that assert the now-valid ClickHouse DDL output. The fix is minimal and well-scoped: it reorders four conditional blocks so LowCardinality always wraps outermost. Both affected branches (FixedString and scalar) are independently exercised in new and updated tests. The only observable behavior change is producing DDL that ClickHouse actually accepts. Existing single-modifier paths (nullable-only, lowCardinality-only) are not touched and their tests remain green. No files require special attention. Important Files Changed
Reviews (1): Last reviewed commit: "Fix ClickHouse LowCardinality + Nullable..." | Re-trigger Greptile |
📊 Coverage
Full per-file breakdown in the job summary. |
What does this PR do?
Fixes the wrapping order the ClickHouse schema compiler emits when a column is both
lowCardinality()andnullable().Bug: the compiler emitted
Nullable(LowCardinality(String)), which ClickHouse rejects withILLEGAL_TYPE_OF_ARGUMENT(verified against clickhouse-server 25.11), so any schema declaring->lowCardinality()->nullable()fails to create. The valid form isLowCardinality(Nullable(String))—LowCardinalitymust be the outermost wrapper.Fix: in
compileColumnType(), applyNullable(...)beforeLowCardinality(...)in both theFixedStringbranch and the scalar type branch, soLowCardinalityalways wraps outermost. Docblock and README examples updated to match.Tests:
LowCardinality(Nullable(String))FixedStringvariant assertingLowCardinality(Nullable(FixedString(2)))Note for #13 (
feat/clickhouse-nested-column-quoting): its consumers (utopia-php/audit#120, utopia-php/usage#4) hit this bug, so #13 should rebase on or merge this fix once it lands.Test Plan
composer test— 5267 tests, 12224 assertions, OKcomposer lint— passcomposer check— PHPStan level max, no errorsRelated PRs and Issues