Safe-change rules¶
The engine validates the computed diff before executing any SQL. These rules block changes that cannot be made safely in place. Each produces a validation failure and a PLANNING_FAILED status with a message naming the rule and the affected column or table. They are the validation-rules layer of the safety model, which explains how they fit alongside declaration-time checks and managed aspects.
Rule |
What it blocks |
How to resolve |
|---|---|---|
|
Adding a |
Add the column as nullable, backfill, set |
|
Changing an existing nullable column to |
Backfill existing NULLs, set |
|
Changing a column’s declared data type outside the widening matrix (type widening) |
Drop and recreate the table out of band, then re-sync |
|
A widening type change without |
Declare the property (it may be set in the same sync as the widen) |
|
Changing |
Rewrite/recreate it, or convert it to clustering out of band, then re-sync |
|
A property transition that is unsafe in place — the catalog rejects it, or applying it would rewrite the table (e.g. |
Update the declaration to match the catalog value |
|
A managed property set on the table but missing from the declaration |
Declare it (or declare it |
|
A plan drops a column but the declaration lacks |
Declare the property (it may be set in the same sync as the drop) |
|
A declared rename whose old and new column both exist on the table |
Remove the |
|
Dropping or changing a primary key while foreign keys reference it (same-table FKs dropped in the same sync are exempt) |
Sync the referencing tables without those foreign keys first, then change the key |
Clustering is not a blocked change¶
Unlike partitioned_by, changing a table’s liquid clustering keys has no
validation rule blocking it: Delta reconciles clustering keys with ALTER TABLE ... CLUSTER BY (...) (or CLUSTER BY NONE to remove them), so the engine plans
this in place. PartitioningChangeNotSupported reflects delta-engine’s
create-only partitioning model. Changing one partition specification to another
requires a data rewrite, and although Databricks Runtime 18.1 and above can
convert a partitioned table to liquid clustering with REPLACE PARTITIONED BY WITH CLUSTER BY, the engine does not model that conversion.
Re-clustering only affects data written after the change: existing files
keep their old clustering layout until they are rewritten by a subsequent
OPTIMIZE (optionally OPTIMIZE FULL to rewrite the whole table
immediately). The engine issues the ALTER TABLE but does not run
OPTIMIZE; query performance on old data improves only once you optimize.
Column renames¶
A rename is declared with renamed_from on the new column (see
renaming a column). The differ
relabels the observed column through the hint before comparing, so a rename
emits a single rename action rather than a drop plus an add. The hint applies
only when the old name is observed and the new one is not; once applied it is
inert, so it is safe to keep as declaration history and correct on a fresh
catalog (remove the hint if the declaration’s scope is later narrowed). If
both names are observed the rename cannot apply and AmbiguousColumnRename
blocks the sync.
Partitioning and clustering metadata follow a mapped column’s identity, so a
layout key rename does not create layout drift. Primary and foreign keys
involving the column are replaced explicitly: the plan drops each key before
the rename and re-adds the declared key afterwards, rather than relying on
the implicit drops Databricks performs during RENAME COLUMN. An inbound
foreign key still blocks a primary-key rename via
PrimaryKeyReferencedByForeignKeys.
Renaming requires delta.columnMapping.mode='name'. Downstream consumers are
affected in ways the engine cannot see and does not manage: streaming reads
need a schemaTrackingLocation to survive a rename, and change data feed has
limitations on column-mapped tables. These are consumer concerns, documented
rather than validated. CHECK constraints and generated columns are also
outside the model: change dependent expressions first, or Databricks rejects
the rename at execution.
Three further checks are scope invariants rather than rules — they define what a declaration is allowed to govern and always run, regardless of the rule set:
Invariant |
What it blocks |
How to resolve |
|---|---|---|
|
An unmanaged aspect (e.g. column structure) has drifted from the declaration in a restricted-scope sync |
Sync the table fully, or update the declaration to match the live schema |
|
The table does not exist but this definition does not manage table existence |
Create the table out-of-band first, or manage it fully |
|
The observed table is a streaming table and the declaration manages more than tags |
Declare it with |
StreamingTableTagsOnly judges the declaration against the observed relation
kind, not against drift, so it fires even when the streaming table is
currently in sync. Comments and properties stay unmanageable on streaming
tables deliberately: the pipeline definition owns them and a refresh can
revert out-of-band changes, whereas Unity Catalog tags persist.
Declaration-time checks¶
Some invalid states are rejected before any sync — a ValueError when the
DeltaTable (or a type) is constructed, because no catalog state could make
them succeed:
Check |
What it rejects |
|---|---|
Nullable primary key |
A primary key column declared |
Duplicate foreign keys |
Two foreign keys over the same local columns |
Foreign key type match |
Local column types differing from the referenced primary key’s column types |
Unmanaged property key |
A property key outside |
Property value format |
A value the catalog would reject: boolean keys ( |
Column and struct field names needing column mapping |
Special characters (spaces, |
Rename hint without column mapping |
A |
Incoherent rename hint |
A |
CDF-reserved column names |
|
Tag limits |
More than 50 tags on the table or a column, or a tag key or value over 256 characters |
Decimal precision |
|
Partitioning |
Partition columns of complex type ( |
Clustering |
More than four |
Validation runs before any SQL executes. A failed validation means the table is unchanged.