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

One row change within a `Replicant.Transaction`.

## Unchanged TOAST
An UPDATE that does not touch a TOASTed column sends a sentinel, not the value.
The sentinel is surfaced as a first-class `unchanged: [col]` list — it **never**
appears in `record`. A sink upserting the row must leave those columns
untouched. For sinks needing the full row on every UPDATE, document
`REPLICA IDENTITY FULL` upstream.

## Replica identity
Under the default identity, a DELETE and an UPDATE's `old_record` carry only
key columns. `old_record` is key-only unless the table is `REPLICA IDENTITY
FULL`.

## String keys
Column names in `record` / `old_record` are **binaries**, never atoms — a wide
or attacker-influenced schema must not exhaust the atom table.

# `op`

```elixir
@type op() :: :insert | :update | :delete | :truncate | :snapshot
```

# `t`

```elixir
@type t() :: %Replicant.Change{
  columns: [Replicant.Change.Column.t()],
  commit_lsn: Replicant.lsn() | nil,
  old_record: %{required(String.t()) =&gt; term()} | nil,
  op: op() | nil,
  ordinal: non_neg_integer(),
  record: %{required(String.t()) =&gt; term()} | nil,
  schema: String.t() | nil,
  table: String.t() | nil,
  unchanged: [String.t()]
}
```

---

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