# `Replicant.SchemaChange`
[🔗](https://github.com/baselabs/replicant/blob/v0.3.1/lib/replicant/schema_change.ex#L1)

Classification of a `Relation` diff (spec §9): **additive** (new column —
auto-apply) or **destructive** (dropped column, type change, replica-identity
change — delegate to the sink or halt fail-closed). A `Truncate` is delivered
as a change, not a schema change.

The Assembler diffs each incoming `Relation` against the cached one and calls
`classify/2`. `nil` means "no schema-relevant change" (e.g. only column order
shifted without drops/type/identity changes — treated as additive-safe).

## Replica-identity changes (spec §7/§9)
A change of replica identity is destructive because it alters the meaning of
`old_record` for every subsequent change. `classify/2` catches it two ways:
the `replica_identity` **enum** value changing (`:default -> :all_columns`), AND
the set of `:key`-flagged columns changing while the enum is unchanged (a
`REPLICA IDENTITY USING INDEX` swap to a different unique index, or a
primary-key change that keeps the same column names). Both classify
`:replica_identity_changed :destructive`.

# `change`

```elixir
@type change() ::
  :column_added | :column_dropped | :type_changed | :replica_identity_changed
```

# `kind`

```elixir
@type kind() :: :additive | :destructive
```

# `t`

```elixir
@type t() :: %Replicant.SchemaChange{
  change: change(),
  detail: String.t() | nil,
  kind: kind(),
  schema: String.t() | nil,
  table: String.t() | nil
}
```

# `classify`

```elixir
@spec classify(
  Replicant.Decoder.Messages.Relation.t() | nil,
  Replicant.Decoder.Messages.Relation.t()
) ::
  t() | nil
```

Diff `old` and `new` `Relation` messages. Returns `%SchemaChange{}` or `nil`
when nothing schema-relevant changed. A replica-identity change dominates
(checked first) because it alters the meaning of `old_record` for every
subsequent change.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
