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 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

Link to this function

build_key(rel, record, pk_cols)

View Source

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"|
Link to this function

convert_update(change, list)

View Source

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}}
Link to this function

filter_columns(change, columns_to_keep)

View Source
@spec filter_columns(change(), [String.t()]) :: change()

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"}}