View Source Electric.Replication.Changes (electric v0.9.5)
This module contains structs that are intermediate representation of Postgres and Satellite transactions.
Some of the core assumptions in this module:
- We require PK always to be present for all tables
- For now PK modification is not supported
- PG replication protocol is expected to always send the whole row when dealing with UPDATE changes, and optionally old row if REPLICA identity is set to FULL.
Summary
Types
Tag has the form of origin@timestamp
, where origin is a unique source id
(UUID for Satellite clients) and timestamp is millisecond-precision UTC unix timestamp
Functions
Build a unique key for a given record based on it's relation and PK.
Convert an UpdatedRecord into the corresponding NewRecord or DeletedRecord
based on the provided to
option.
Filter the columns of a change to include only those provided in columns_to_keep
.
Types
@type change() :: data_change() | Electric.Replication.Changes.TruncatedRelation.t()
@type data_change() :: Electric.Replication.Changes.NewRecord.t() | Electric.Replication.Changes.UpdatedRecord.t() | Electric.Replication.Changes.DeletedRecord.t()
@type db_identifier() :: String.t()
@type pk() :: [String.t(), ...]
@type record() :: %{ required(column_name :: db_identifier()) => column_data :: binary() }
@type relation_id() :: non_neg_integer()
@type relation_name() :: {schema :: db_identifier(), table :: db_identifier()}
@type tag() :: String.t()
Tag has the form of origin@timestamp
, where origin is a unique source id
(UUID for Satellite clients) and timestamp is millisecond-precision UTC unix timestamp
@type xid() :: non_neg_integer()
Functions
Build a unique key for a given record based on it's relation and PK.
Uses the /
symbol as a PK separator, so any /
s in the PK will
be escaped to avoid collisions.
Examples
Build key respects PK column order:
iex> build_key({"hello", "world"}, %{"c" => "d", "a" => "b"}, ["a", "c"])
~S|"hello"."world"/"b"/"d"|
iex> build_key({"hello", "world"}, %{"a" => "b", "c" => "d"}, ["a", "c"])
~S|"hello"."world"/"b"/"d"|
Build key has /
symbol in the PK escaped by repetition:
iex> build_key({"hello", "world"}, %{"a" => "test/test", "c" => "test"}, ["a", "c"])
~S|"hello"."world"/"test//test"/"test"|
iex> build_key({"hello", "world"}, %{"a" => "test", "c" => "test/test"}, ["a", "c"])
~S|"hello"."world"/"test"/"test//test"|
If a table has no PK, all columns are used, sorted by the column name:
iex> build_key({"hello", "world"}, %{"c" => "d", "a" => "b"}, [])
~S|"hello"."world"/"b"/"d"|
All pk sections are wrapped in quotes to allow for empty strings without generating a //
pair.
iex> build_key({"hello", "world"}, %{"a" => "1", "b" => "", "c" => "2"}, [])
~S|"hello"."world"/"1"/""/"2"|
Convert an UpdatedRecord into the corresponding NewRecord or DeletedRecord
based on the provided to
option.
Examples
iex> convert_update(%UpdatedRecord{record: %{id: 1}}, to: :new_record)
%NewRecord{record: %{id: 1}}
iex> convert_update(%UpdatedRecord{record: %{id: 2}, old_record: %{id: 1}}, to: :deleted_record)
%DeletedRecord{old_record: %{id: 1}}
iex> convert_update(%UpdatedRecord{record: %{id: 1}}, to: :updated_record)
%UpdatedRecord{record: %{id: 1}}
Filter the columns of a change to include only those provided in columns_to_keep
.
Examples
iex> filter_columns(%NewRecord{record: %{"a" => "b", "c" => "d"}}, ["a"])
%NewRecord{record: %{"a" => "b"}}
iex> filter_columns(UpdatedRecord.new(
...> record: %{"a" => "b", "c" => "d"},
...> old_record: %{"a" => "d", "c" => "f"}
...> ), ["a"])
UpdatedRecord.new(record: %{"a" => "b"}, old_record: %{"a" => "d"})
iex> filter_columns(%DeletedRecord{old_record: %{"a" => "b", "c" => "d"}}, ["c"])
%DeletedRecord{old_record: %{"c" => "d"}}