Skip to content

fix: Contract enforcement for FixedString(N) columns - #727

Open
bartcode wants to merge 4 commits into
ClickHouse:mainfrom
bartcode:fix/fixedstring-contract
Open

fix: Contract enforcement for FixedString(N) columns#727
bartcode wants to merge 4 commits into
ClickHouse:mainfrom
bartcode:fix/fixedstring-contract

Conversation

@bartcode

Copy link
Copy Markdown
Contributor

Fixes #726

Problem

ClickHouseColumn.data_type rendered every string-family column as String because string_type() ignores its size argument, so FixedString(N) collapsed to String. Contract enforcement compares data_type strings verbatim against YAML data_type declarations, which produced two bugs (#726):

  • a FixedString(N) contract failed against a FixedString(N) model column with "data type mismatch"
  • a String contract silently passed against a FixedString(N) column; the table was then created with a String column while the model produces FixedString(N), and later incremental runs failed with a misleading New column types: ['col String'] error, since the drift message renders the type through the same collapsing property

Change

data_type now renders parameterized FixedString(N) exactly (FixedString(16) instead of String). Bare FixedString (not a valid ClickHouse type) and plain String keep their previous rendering, and string_type()/string_size() are unchanged, so schema-expansion checks (can_expand_to) are unaffected.

Side effects (all improvements)

  • append_new_columns/sync_all_columns ALTERs now use the exact type (ADD/MODIFY COLUMN ... FixedString(N) is valid ClickHouse DDL) instead of silently widening the column to String. The row in dbt Core 2.0 parity: Materialization incremental #704's Fusion parity table documents the old rendering and should be updated when this lands.
  • check_incremental_schema_changes error messages now display the actual column type instead of a collapsed String.

Testing

  • Updated column unit tests to pin FixedString(16) rendering, plain and through Nullable/LowCardinality wrappers; fixed a stale assertion in test_low_cardinality_nullable_type that only held because of the collapse.
  • Added a FixedString(16) case to the contract data_types fixture, exercised across table/view/distributed materializations (both matching and mismatching paths); verified the new test fails without the code fix.
  • make lint clean; 92 unit/column tests and 21 contract integration tests pass against dockerized ClickHouse.

ClickHouseColumn.data_type collapsed FixedString(N) to String, so
enforced contracts failed with a 'data type mismatch' when the YAML
contract declared FixedString(N), while a String contract incorrectly
passed against a FixedString(N) column. Render parameterized
FixedString exactly so the verbatim type comparison in
clickhouse__get_assert_columns_equivalent matches.
Mirror the numeric_type pattern: is_fixed_string dispatches to a
dedicated fixedstring_type(size) classmethod instead of an inline
conditional in data_type. No behavior change.
@bartcode bartcode changed the title Fix contract enforcement for FixedString(N) columns fix: Contract enforcement for FixedString(N) columns Aug 27, 2026
@koletzilla
koletzilla requested review from koletzilla and a balanced review from Copilot August 27, 2026 12:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Preserves FixedString(N) type parameters for contract enforcement and schema-change handling.

Changes:

  • Render sized FixedString types exactly.
  • Add column and contract regression coverage.
  • Document the fix in the changelog.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
dbt/adapters/clickhouse/column.py Preserves sized FixedString rendering.
tests/integration/adapter/column_types/test_column_types.py Tests direct and wrapped rendering.
tests/integration/adapter/constraints/test_constraints.py Adds contract coverage.
CHANGELOG.md Records the bug fix.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

return [
["1::Int32", "Int32", "Int32"],
["'1'", "String", "String"],
["toFixedString('1', 16)", "FixedString(16)", "FixedString(16)"],

@bartcode bartcode Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. The Int128 loop could never catch this direction. Added a test in 1126e84: the model produces toFixedString('1', 16) and the contract declares String, and the run is expected to fail preflight with 'data type mismatch'. Confirmed it fails without the fix (that's exactly the silent pass from #726) and passes with it.

Comment on lines +57 to +60
if self.is_fixed_string():
# Contract enforcement compares data_type strings verbatim against
# YAML data_type declarations, so the exact type must be preserved
data_t = self.fixedstring_type(self.string_size())

@bartcode bartcode Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in the same commit: TestFixedStringSchemaChange covers append_new_columns and sync_all_columns with FixedString columns, for both regular and distributed incremental. It checks system.columns after the ALTERs. A newly added column has to come in as FixedString(16), and a sync from FixedString(16) to FixedString(32) has to end up as FixedString(32). Also made sure a third run stays clean, since without the fix the column was added as String and the next run hit the drift error.

Cover the two gaps from the PR ClickHouse#727 review:
- a String contract now has a preflight-failure test against a
  FixedString(N) model column (previously passed silently, ClickHouse#726)
- append_new_columns ADD COLUMN and sync_all_columns MODIFY COLUMN are
  pinned to the exact FixedString(N) type in ALTER DDL, including a
  clean third incremental run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FixedString(N) contract columns always fail with "data type mismatch"

2 participants