Skip to content

Index

Typed-diff pipeline: walk two states, resolve dependencies, render SQL.

AzureDatabricksOptions

Bases: BaseModel

Azure Databricks-specific render-time options grouped by object type.

These options are not tracked in state and are not crawled from the database. They influence rendering only — currently only the CREATE TABLE statement (via tables.properties). As a result, changes to these options apply to newly created objects only; existing objects are left untouched. Future object groups (views etc.) will be added as sibling sub-blocks.

Parameters:

Name Type Description Default
tables

Options applied to table-creation statements.

required
Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
class AzureDatabricksOptions(BaseModel):
    """Azure Databricks-specific render-time options grouped by object type.

    These options are *not* tracked in state and are not crawled from the
    database. They influence rendering only — currently only the
    ``CREATE TABLE`` statement (via ``tables.properties``). As a result,
    changes to these options apply to newly created objects only; existing
    objects are left untouched. Future object groups (``views`` etc.) will
    be added as sibling sub-blocks.

    Args:
        tables: Options applied to table-creation statements.

    """

    model_config = ConfigDict(extra="forbid")

    tables: TableOptions = Field(default_factory=TableOptions)

ColumnAdded

Bases: Diff

A new column appeared on an existing or newly-added table.

Source code in src/cloe_delta_table_manager/diff/diff.py
class ColumnAdded(Diff):
    """A new column appeared on an existing or newly-added table."""

    column: Column
    parent_table: Table
    parent_schema: Schema
    parent_db: Database

ColumnChanged

Bases: Diff

A column matched by name has one or more differing fields between states.

Source code in src/cloe_delta_table_manager/diff/diff.py
class ColumnChanged(Diff):
    """A column matched by name has one or more differing fields between states."""

    old: Column
    new: Column
    parent_table: Table
    parent_schema: Schema
    parent_db: Database

ColumnRemoved

Bases: Diff

A column present in current state is gone in desired state.

Source code in src/cloe_delta_table_manager/diff/diff.py
class ColumnRemoved(Diff):
    """A column present in current state is gone in desired state."""

    column: Column
    parent_table: Table
    parent_schema: Schema
    parent_db: Database

DatabaseAdded

Bases: Diff

A new database (Databricks catalog) appeared in the desired state.

Source code in src/cloe_delta_table_manager/diff/diff.py
class DatabaseAdded(Diff):
    """A new database (Databricks catalog) appeared in the desired state."""

    database: Database

DatabaseChanged

Bases: Diff

A database matched by name has differing metadata between states.

Source code in src/cloe_delta_table_manager/diff/diff.py
class DatabaseChanged(Diff):
    """A database matched by name has differing metadata between states."""

    old: Database
    new: Database

DatabaseRemoved

Bases: Diff

A database (Databricks catalog) present in current state is gone in desired state.

Source code in src/cloe_delta_table_manager/diff/diff.py
class DatabaseRemoved(Diff):
    """A database (Databricks catalog) present in current state is gone in desired state."""

    database: Database

DatabricksStatementGenerator

Render typed diffs to Databricks SQL via in-code match-dispatch.

Unsupported deltas are detected generically: a per-class registry of handled fields runs as a pre-check before the match dispatch. Any *Changed diff that touches a field outside its allow-list is reported as unsupported — no silent no-ops, even for diff types added in the future. To add support for a new field on an existing *Changed class, register it in :attr:_HANDLED_CHANGED_FIELDS and extend the renderer.

Parameters:

Name Type Description Default
on_unsupported OnUnsupported

"fail" (default) raises :class:UnsupportedDiffError for diffs without a SQL representation. "skip" returns an empty command list for those diffs; pipeline-level cascading of the skip to dependent diffs is handled by the caller.

'fail'
Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
class DatabricksStatementGenerator:
    """Render typed diffs to Databricks SQL via in-code match-dispatch.

    Unsupported deltas are detected *generically*: a per-class registry of
    handled fields runs as a pre-check before the match dispatch. Any
    ``*Changed`` diff that touches a field outside its allow-list is reported
    as unsupported — no silent no-ops, even for diff types added in the
    future. To add support for a new field on an existing ``*Changed`` class,
    register it in :attr:`_HANDLED_CHANGED_FIELDS` and extend the renderer.

    Args:
        on_unsupported: ``"fail"`` (default) raises :class:`UnsupportedDiffError`
            for diffs without a SQL representation. ``"skip"`` returns an empty
            command list for those diffs; pipeline-level cascading of the skip
            to dependent diffs is handled by the caller.

    """

    # Per-class allow-list of column/table/schema fields whose changes this
    # generator can express in SQL. Anything outside this set surfaces as
    # `UnsupportedDiffError`. Adding a new ``*Changed`` diff class with no
    # entry here means it is always unsupported until rendering is added.
    _HANDLED_CHANGED_FIELDS: ClassVar[dict[type[Diff], frozenset[str]]] = {
        ColumnChanged: frozenset(
            {
                "data_type",
                "data_type_length",
                "data_type_precision",
                "data_type_numeric_scale",
                "is_nullable",
                "comment",
                "constraints",
            }
        ),
        # `is_key` is intentionally absent: primary keys are table-level and
        # rendered from PrimaryKeyChanged, not from a per-column ColumnChanged.
        # TableChanged and SchemaChanged: no fields handled today — any change
        # surfaces as unsupported. Add fields here as renderers are added.
    }

    def __init__(
        self,
        *,
        on_unsupported: OnUnsupported = "fail",
        options: AzureDatabricksOptions | None = None,
    ) -> None:
        """Initialize the generator with an unsupported-diff handling mode and options."""
        self._on_unsupported = on_unsupported
        self._options = options or AzureDatabricksOptions()

    def generate(self, diff: Diff) -> list[str]:  # noqa: PLR0911
        """Return the SQL commands for a single diff (zero, one, or many)."""
        catalog_reason = (
            "catalogs are assumed to exist and are not managed by dtm (no CREATE/ALTER/DROP CATALOG is emitted)"
        )

        # Generic deny-by-default guard for any *Changed-style diff (anything
        # carrying `old`/`new` Pydantic models). Field deltas outside the
        # per-class allow-list surface as unsupported regardless of whether
        # the diff type is known to this generator.
        unsupported = self._check_changed_fields(diff)
        if unsupported is not None:
            return unsupported

        match diff:
            case DatabaseAdded() | DatabaseRemoved() | DatabaseChanged():
                return self._handle_unsupported(diff, catalog_reason)

            case SchemaAdded(schema_obj=sch, parent_db=db):
                return [f"CREATE SCHEMA IF NOT EXISTS {db.name}.{sch.name};"]
            case SchemaRemoved(schema_obj=sch, parent_db=db):
                return [f"DROP SCHEMA IF EXISTS {db.name}.{sch.name} CASCADE;"]

            case TableAdded(table=tbl, parent_schema=sch, parent_db=db):
                return [
                    _render_create_table(
                        db.name,
                        sch.name,
                        tbl.name,
                        tbl.columns,
                        table_properties=self._options.tables.properties,
                    )
                ]
            case TableRemoved(table=tbl, parent_schema=sch, parent_db=db):
                return [f"DROP TABLE IF EXISTS {db.name}.{sch.name}.{tbl.name};"]

            case ColumnAdded(column=col, parent_table=tbl, parent_schema=sch, parent_db=db):
                return _render_add_column(db.name, sch.name, tbl.name, col)
            case ColumnRemoved(column=col, parent_table=tbl, parent_schema=sch, parent_db=db):
                return [f"ALTER TABLE {db.name}.{sch.name}.{tbl.name} DROP COLUMN IF EXISTS {col.name};"]
            case ColumnChanged(old=old, new=new, parent_table=tbl, parent_schema=sch, parent_db=db):
                return _render_alter_column(db.name, sch.name, tbl.name, old, new)

            case PrimaryKeyChanged(
                old_key_columns=old_pk, new_key_columns=new_pk, parent_table=tbl, parent_schema=sch, parent_db=db
            ):
                return _render_primary_key_change(db.name, sch.name, tbl.name, old_pk, new_pk)

            case _:
                return self._handle_unsupported(diff, f"no handler for {type(diff).__name__}")

    def _check_changed_fields(self, diff: Diff) -> list[str] | None:
        """Return the unsupported result if `diff` carries unhandled field deltas.

        Operates on any diff with ``old``/``new`` attributes (the *Changed
        family). Subclasses with no entry in :attr:`_HANDLED_CHANGED_FIELDS`
        are treated as having no handled fields — any delta is unsupported.
        Returns ``None`` if all field deltas are within the allow-list (or
        the diff is not a ``*Changed`` diff).
        """
        old = getattr(diff, "old", None)
        new = getattr(diff, "new", None)
        if not isinstance(old, BaseModel) or not isinstance(new, BaseModel):
            return None
        handled = self._HANDLED_CHANGED_FIELDS.get(type(diff), frozenset())
        unhandled = sorted(_differing_fields(old, new) - handled)
        if not unhandled:
            return None
        return self._handle_unsupported(
            diff,
            f"{type(diff).__name__} touches fields with no SQL renderer: {unhandled}",
        )

    def _handle_unsupported(self, diff: Diff, reason: str) -> list[str]:
        if self._on_unsupported == "fail":
            raise UnsupportedDiffError(diff, reason)
        return []

__init__(*, on_unsupported='fail', options=None)

Initialize the generator with an unsupported-diff handling mode and options.

Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
def __init__(
    self,
    *,
    on_unsupported: OnUnsupported = "fail",
    options: AzureDatabricksOptions | None = None,
) -> None:
    """Initialize the generator with an unsupported-diff handling mode and options."""
    self._on_unsupported = on_unsupported
    self._options = options or AzureDatabricksOptions()

generate(diff)

Return the SQL commands for a single diff (zero, one, or many).

Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
def generate(self, diff: Diff) -> list[str]:  # noqa: PLR0911
    """Return the SQL commands for a single diff (zero, one, or many)."""
    catalog_reason = (
        "catalogs are assumed to exist and are not managed by dtm (no CREATE/ALTER/DROP CATALOG is emitted)"
    )

    # Generic deny-by-default guard for any *Changed-style diff (anything
    # carrying `old`/`new` Pydantic models). Field deltas outside the
    # per-class allow-list surface as unsupported regardless of whether
    # the diff type is known to this generator.
    unsupported = self._check_changed_fields(diff)
    if unsupported is not None:
        return unsupported

    match diff:
        case DatabaseAdded() | DatabaseRemoved() | DatabaseChanged():
            return self._handle_unsupported(diff, catalog_reason)

        case SchemaAdded(schema_obj=sch, parent_db=db):
            return [f"CREATE SCHEMA IF NOT EXISTS {db.name}.{sch.name};"]
        case SchemaRemoved(schema_obj=sch, parent_db=db):
            return [f"DROP SCHEMA IF EXISTS {db.name}.{sch.name} CASCADE;"]

        case TableAdded(table=tbl, parent_schema=sch, parent_db=db):
            return [
                _render_create_table(
                    db.name,
                    sch.name,
                    tbl.name,
                    tbl.columns,
                    table_properties=self._options.tables.properties,
                )
            ]
        case TableRemoved(table=tbl, parent_schema=sch, parent_db=db):
            return [f"DROP TABLE IF EXISTS {db.name}.{sch.name}.{tbl.name};"]

        case ColumnAdded(column=col, parent_table=tbl, parent_schema=sch, parent_db=db):
            return _render_add_column(db.name, sch.name, tbl.name, col)
        case ColumnRemoved(column=col, parent_table=tbl, parent_schema=sch, parent_db=db):
            return [f"ALTER TABLE {db.name}.{sch.name}.{tbl.name} DROP COLUMN IF EXISTS {col.name};"]
        case ColumnChanged(old=old, new=new, parent_table=tbl, parent_schema=sch, parent_db=db):
            return _render_alter_column(db.name, sch.name, tbl.name, old, new)

        case PrimaryKeyChanged(
            old_key_columns=old_pk, new_key_columns=new_pk, parent_table=tbl, parent_schema=sch, parent_db=db
        ):
            return _render_primary_key_change(db.name, sch.name, tbl.name, old_pk, new_pk)

        case _:
            return self._handle_unsupported(diff, f"no handler for {type(diff).__name__}")

Diff

Bases: BaseModel

Base class for all database-object diffs.

Carries only identity (id) and dependency wiring (depends_on). Semantics live entirely in the subclass.

Source code in src/cloe_delta_table_manager/diff/diff.py
class Diff(BaseModel):
    """Base class for all database-object diffs.

    Carries only identity (`id`) and dependency wiring (`depends_on`).
    Semantics live entirely in the subclass.
    """

    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: UUID = Field(default_factory=uuid4)
    depends_on: list[UUID] = Field(default_factory=list)

DiffDependencyResolver

Wires depends_on between typed diffs based on containment.

Source code in src/cloe_delta_table_manager/diff/diff_dependency_resolver.py
class DiffDependencyResolver:
    """Wires `depends_on` between typed diffs based on containment."""

    def resolve(self, diffs: list[Diff]) -> list[Diff]:
        """Populate `depends_on` in place and return the same list."""
        for diff in diffs:
            for other in diffs:
                if diff is other:
                    continue
                if _depends_on(diff, other):
                    diff.depends_on.append(other.id)
        return diffs

resolve(diffs)

Populate depends_on in place and return the same list.

Source code in src/cloe_delta_table_manager/diff/diff_dependency_resolver.py
def resolve(self, diffs: list[Diff]) -> list[Diff]:
    """Populate `depends_on` in place and return the same list."""
    for diff in diffs:
        for other in diffs:
            if diff is other:
                continue
            if _depends_on(diff, other):
                diff.depends_on.append(other.id)
    return diffs

DiffResolver

Walk two DatabaseState trees and emit typed diffs.

Emission order is top-down: parent diffs appear before their children in the returned list. Execution ordering for downstream stages is handled by :class:DiffDependencyResolver via depends_on.

Source code in src/cloe_delta_table_manager/diff/diff_resolver.py
class DiffResolver:
    """Walk two DatabaseState trees and emit typed diffs.

    Emission order is top-down: parent diffs appear before their children in
    the returned list. Execution ordering for downstream stages is handled by
    :class:`DiffDependencyResolver` via ``depends_on``.
    """

    def diff(self, current: DatabaseState, desired: DatabaseState) -> list[Diff]:
        """Return the typed diff list to transform ``current`` into ``desired``."""
        diffs: list[Diff] = []
        self._diff_databases(current, desired, diffs)
        return diffs

    def _diff_databases(self, current: Databases, desired: Databases, out: list[Diff]) -> None:
        """Descend into every database present in either state.

        Catalogs (databases) are assumed to exist outside dtm's control — see
        ``docs/decisions/migration-generation.md``. The walker therefore never
        emits ``DatabaseAdded`` / ``DatabaseRemoved`` / ``DatabaseChanged``;
        it only walks into the schemas/tables/columns inside each catalog.
        """
        current_by_name = {db.name: db for db in current.databases}
        desired_by_name = {db.name: db for db in desired.databases}

        for name in sorted(current_by_name.keys() | desired_by_name.keys()):
            current_db = current_by_name.get(name)
            desired_db = desired_by_name.get(name)
            # Use whichever Database is present as the parent reference for
            # children — preferring `desired` since it represents the goal
            # state used in downstream rendering.
            parent_db = desired_db or current_db
            assert parent_db is not None  # name came from the union of the two maps
            current_schemas = current_db.schemas if current_db else []
            desired_schemas = desired_db.schemas if desired_db else []
            self._diff_schemas(parent_db, current_schemas, desired_schemas, out)

    def _diff_schemas(self, parent_db: Database, current: list[Schema], desired: list[Schema], out: list[Diff]) -> None:
        current_by_name = {s.name: s for s in current}
        desired_by_name = {s.name: s for s in desired}

        for name in sorted(desired_by_name.keys() - current_by_name.keys()):
            self._cascade_schema_added(desired_by_name[name], parent_db, out)
        for name in sorted(current_by_name.keys() - desired_by_name.keys()):
            self._cascade_schema_removed(current_by_name[name], parent_db, out)
        for name in sorted(current_by_name.keys() & desired_by_name.keys()):
            old, new = current_by_name[name], desired_by_name[name]
            if _differ(old, new, _SCHEMA_EXCLUDE):
                out.append(SchemaChanged(old=old, new=new, parent_db=parent_db))
            self._diff_schema_contents(parent_db, new, old, new, out)

    def _diff_schema_contents(
        self,
        parent_db: Database,
        parent_schema: Schema,
        current_schema: Schema,
        desired_schema: Schema,
        out: list[Diff],
    ) -> None:
        """Dispatch over peer object types within a schema.

        Today only tables exist at this level; views, materialized views and
        sequences extend this dispatch when ``cloe_metadata`` supports them.
        """
        self._diff_tables(parent_db, parent_schema, current_schema.tables, desired_schema.tables, out)

    def _diff_tables(
        self,
        parent_db: Database,
        parent_schema: Schema,
        current: list[Table],
        desired: list[Table],
        out: list[Diff],
    ) -> None:
        current_by_name = {t.name: t for t in current}
        desired_by_name = {t.name: t for t in desired}

        for name in sorted(desired_by_name.keys() - current_by_name.keys()):
            self._cascade_table_added(desired_by_name[name], parent_schema, parent_db, out)
        for name in sorted(current_by_name.keys() - desired_by_name.keys()):
            self._cascade_table_removed(current_by_name[name], parent_schema, parent_db, out)
        for name in sorted(current_by_name.keys() & desired_by_name.keys()):
            old, new = current_by_name[name], desired_by_name[name]
            if _differ(old, new, _TABLE_EXCLUDE):
                out.append(TableChanged(old=old, new=new, parent_schema=parent_schema, parent_db=parent_db))
            self._diff_columns(parent_db, parent_schema, new, old.columns, new.columns, out)
            self._diff_primary_key(parent_db, parent_schema, old, new, out)

    def _diff_columns(  # noqa: PLR0913
        self,
        parent_db: Database,
        parent_schema: Schema,
        parent_table: Table,
        current: list[Column],
        desired: list[Column],
        out: list[Diff],
    ) -> None:
        current_by_name = {c.name: c for c in current}
        desired_by_name = {c.name: c for c in desired}

        for name in sorted(desired_by_name.keys() - current_by_name.keys()):
            out.append(
                ColumnAdded(
                    column=desired_by_name[name],
                    parent_table=parent_table,
                    parent_schema=parent_schema,
                    parent_db=parent_db,
                )
            )
        for name in sorted(current_by_name.keys() - desired_by_name.keys()):
            out.append(
                ColumnRemoved(
                    column=current_by_name[name],
                    parent_table=parent_table,
                    parent_schema=parent_schema,
                    parent_db=parent_db,
                )
            )
        for name in sorted(current_by_name.keys() & desired_by_name.keys()):
            old, new = current_by_name[name], desired_by_name[name]
            if _differ(old, new, _COLUMN_EXCLUDE):
                out.append(
                    ColumnChanged(
                        old=old,
                        new=new,
                        parent_table=parent_table,
                        parent_schema=parent_schema,
                        parent_db=parent_db,
                    )
                )

    def _diff_primary_key(
        self,
        parent_db: Database,
        parent_schema: Schema,
        old_table: Table,
        new_table: Table,
        out: list[Diff],
    ) -> None:
        """Emit a single PrimaryKeyChanged when the table's key-column set changes."""
        old_pk = [c.name for c in old_table.columns if c.is_key]
        new_pk = [c.name for c in new_table.columns if c.is_key]
        if old_pk != new_pk:
            out.append(
                PrimaryKeyChanged(
                    old_key_columns=old_pk,
                    new_key_columns=new_pk,
                    parent_table=new_table,
                    parent_schema=parent_schema,
                    parent_db=parent_db,
                )
            )

    # ── cascade helpers ──────────────────────────────────────────────────────

    def _cascade_schema_added(self, schema: Schema, parent_db: Database, out: list[Diff]) -> None:
        out.append(SchemaAdded(schema_obj=schema, parent_db=parent_db))
        for table in schema.tables:
            self._cascade_table_added(table, schema, parent_db, out)

    def _cascade_schema_removed(self, schema: Schema, parent_db: Database, out: list[Diff]) -> None:
        out.append(SchemaRemoved(schema_obj=schema, parent_db=parent_db))
        for table in schema.tables:
            self._cascade_table_removed(table, schema, parent_db, out)

    def _cascade_table_added(self, table: Table, parent_schema: Schema, parent_db: Database, out: list[Diff]) -> None:
        # CREATE TABLE renders columns inline, so no ColumnAdded cascade.
        out.append(TableAdded(table=table, parent_schema=parent_schema, parent_db=parent_db))

    def _cascade_table_removed(self, table: Table, parent_schema: Schema, parent_db: Database, out: list[Diff]) -> None:
        # DROP TABLE removes columns implicitly, so no ColumnRemoved cascade.
        out.append(TableRemoved(table=table, parent_schema=parent_schema, parent_db=parent_db))

diff(current, desired)

Return the typed diff list to transform current into desired.

Source code in src/cloe_delta_table_manager/diff/diff_resolver.py
def diff(self, current: DatabaseState, desired: DatabaseState) -> list[Diff]:
    """Return the typed diff list to transform ``current`` into ``desired``."""
    diffs: list[Diff] = []
    self._diff_databases(current, desired, diffs)
    return diffs

PrimaryKeyChanged

Bases: Diff

The primary-key column set of a table changed between states.

A primary key is a table-level concept — a table has at most one primary key, possibly spanning several columns — so it is modelled as a single table-scoped diff rather than per-column is_key flips. old_key_columns and new_key_columns hold the ordered key-column names; an empty list means "no primary key" on that side.

Source code in src/cloe_delta_table_manager/diff/diff.py
class PrimaryKeyChanged(Diff):
    """The primary-key column set of a table changed between states.

    A primary key is a table-level concept — a table has at most one primary
    key, possibly spanning several columns — so it is modelled as a single
    table-scoped diff rather than per-column ``is_key`` flips. ``old_key_columns``
    and ``new_key_columns`` hold the ordered key-column names; an empty list
    means "no primary key" on that side.
    """

    old_key_columns: list[str]
    new_key_columns: list[str]
    parent_table: Table
    parent_schema: Schema
    parent_db: Database

SchemaAdded

Bases: Diff

A new schema appeared under an existing or newly-added database.

Source code in src/cloe_delta_table_manager/diff/diff.py
class SchemaAdded(Diff):
    """A new schema appeared under an existing or newly-added database."""

    schema_obj: Schema
    parent_db: Database

SchemaChanged

Bases: Diff

A schema matched by name has differing metadata between states.

Source code in src/cloe_delta_table_manager/diff/diff.py
class SchemaChanged(Diff):
    """A schema matched by name has differing metadata between states."""

    old: Schema
    new: Schema
    parent_db: Database

SchemaRemoved

Bases: Diff

A schema present in current state is gone in desired state.

Source code in src/cloe_delta_table_manager/diff/diff.py
class SchemaRemoved(Diff):
    """A schema present in current state is gone in desired state."""

    schema_obj: Schema
    parent_db: Database

TableAdded

Bases: Diff

A new table appeared under an existing or newly-added schema.

Source code in src/cloe_delta_table_manager/diff/diff.py
class TableAdded(Diff):
    """A new table appeared under an existing or newly-added schema."""

    table: Table
    parent_schema: Schema
    parent_db: Database

TableChanged

Bases: Diff

A table matched by name has differing metadata between states.

Source code in src/cloe_delta_table_manager/diff/diff.py
class TableChanged(Diff):
    """A table matched by name has differing metadata between states."""

    old: Table
    new: Table
    parent_schema: Schema
    parent_db: Database

TableOptions

Bases: BaseModel

Render-time options that apply to CREATE TABLE statements.

Parameters:

Name Type Description Default
properties

Key-value pairs emitted as TBLPROPERTIES on every generated CREATE TABLE statement. Values are quoted as strings.

required
Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
class TableOptions(BaseModel):
    """Render-time options that apply to ``CREATE TABLE`` statements.

    Args:
        properties: Key-value pairs emitted as ``TBLPROPERTIES`` on every
            generated ``CREATE TABLE`` statement. Values are quoted as strings.

    """

    model_config = ConfigDict(extra="forbid")

    properties: dict[str, str] = Field(default_factory=dict)

TableRemoved

Bases: Diff

A table present in current state is gone in desired state.

Source code in src/cloe_delta_table_manager/diff/diff.py
class TableRemoved(Diff):
    """A table present in current state is gone in desired state."""

    table: Table
    parent_schema: Schema
    parent_db: Database

UnsupportedDiffError

Bases: Exception

Raised when a diff has no Databricks SQL representation.

Carries the offending diff so the caller can decide how to surface it.

Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
class UnsupportedDiffError(Exception):
    """Raised when a diff has no Databricks SQL representation.

    Carries the offending diff so the caller can decide how to surface it.
    """

    def __init__(self, diff: Diff, reason: str) -> None:
        """Build the error with the offending diff and a human-readable reason."""
        self.diff = diff
        self.reason = reason
        super().__init__(f"Unsupported diff {type(diff).__name__}: {reason}")

__init__(diff, reason)

Build the error with the offending diff and a human-readable reason.

Source code in src/cloe_delta_table_manager/diff/databricks_statement_generator.py
def __init__(self, diff: Diff, reason: str) -> None:
    """Build the error with the offending diff and a human-readable reason."""
    self.diff = diff
    self.reason = reason
    super().__init__(f"Unsupported diff {type(diff).__name__}: {reason}")