# `Sublimate.ProjectionData`

Describes a projection to the engine: its source and destination tables, join
chain, identity column, and the strategy that defines what to derive.

A consumer builds a `ProjectionData` (usually through a small builder that maps
its own config onto this shape) and hands it to `Sublimate.Projection` to
install and run.

## Struct fields by type

  * **source**

    `:qualified_source_table`, `:source_table_alias`, `:identity_column`,
    `:add_identity_column_if_not_exists`.

    The identity column is the backbone of a projection. Every projection row
    is keyed by the source identity, so the engine verifies that the identity
    column exists before installation.

  * **destination**

    `:qualified_destination_table`, `:destination_table_unique_index`.

    The destination table contains the projection's output. The consumer creates and
    owns this table; the engine only maintains its contents.

  * **deltas**

    `:qualified_deltas_table`, `:deltas_table_unique_index`.

    The deltas table stores pending changes before they are merged into the
    destination table. Its column structure is strategy-defined; these fields only
    identify the database objects managed by the engine.

  * **database functions**

    `:qualified_destination_trigger_fn` (the source-table trigger function that maintains the destination),
    `:qualified_insert_merge_deltas_fn` (the merge function).

  * **database triggers**

    `:delta_trigger` (the source-table trigger), `:join_triggers` (per-joined-table trigger and function names, keyed by
    table).

  * **strategy**

    The `:strategy` module and its `:strategy_data` belong together - the strategy
    is the only thing that interprets its own configuration - so they are set as a
    pair.

## Construction

`new/1` transforms the input config into a `ProjectionData` struct.
It resolves qualified table names and the names of
database objects managed by the engine, but does not inspect the database.

Schema-dependent work and derived descriptors are deferred to the strategy
module's `c:Sublimate.ProjectionStrategy.prepare/3` callback during
installation.

Typically, a consumer maps its domain-specific configuration to this shape using
a small builder instead of calling `new/1` directly. The builder sets `:strategy`
to the implementing module and `:strategy_data` to that implementation's configuration.

# `config`

```elixir
@type config() :: %{
  destination_table: String.t(),
  identity_column: String.t(),
  source_table: String.t(),
  strategy_data: map(),
  strategy: module(),
  add_identity_column_if_not_exists: boolean() | nil,
  source_name: String.t() | nil
}
```

Input data to generate a `ProjectionData` struct.

### Config fields

#### Required fields

  * `destination_table`

      The name of the destination table, with an optional database schema prefix.

  * `identity_column`

      The name of the column in the source table that uniquely identifies each row.
      This column must be of an integer type - a regular integer or a PostgreSQL identity column.
      Uniqueness must be enforced by a `PRIMARY KEY` or `UNIQUE` constraint.

      Will be added automatically to the source table when missing and field `add_identity_column_if_not_exists` is `true`.

  * `source_table`

      The name of the source table, with an optional database schema prefix.

  * `strategy`

      The module that implements `Sublimate.ProjectionStrategy` behaviour.

  * `strategy_data`

      Data that is unique to the strategy module implementation. For example,
      with Sublimate it contains a facets configuration.

#### Optional fields

  * `source_name`

      A discriminator inserted into this projection's source-side trigger and function names,
      so multiple projections sharing one destination table do not collide.

      Omit for a single-source projection.

      Must be a valid identifier fragment (letters, digits, underscores; no leading digit, no hyphens);
      it becomes part of a PostgreSQL identifier.

  * `add_identity_column_if_not_exists`

      If `true`, adds an auto-increment identity column to the source table, using the column name provided via field `identity_column`.

# `join_spec`

```elixir
@type join_spec() :: %{
  table: String.t(),
  match: String.t(),
  to: String.t(),
  where: String.t() | nil
}
```

# `join_triggers`

```elixir
@type join_triggers() :: %{
  required(String.t()) =&gt; %{
    delete_trigger: String.t(),
    trigger_fn: String.t(),
    trigger: String.t()
  }
}
```

# `postgrex_options`

```elixir
@type postgrex_options() :: [{:timeout, integer() | :infinity}]
```

# `t`

```elixir
@type t() :: %Sublimate.ProjectionData{
  add_identity_column_if_not_exists: boolean() | nil,
  delta_trigger: String.t(),
  deltas_table_unique_index: String.t(),
  destination_table_unique_index: String.t(),
  identity_column: String.t(),
  join_triggers: join_triggers(),
  qualified_deltas_table: String.t(),
  qualified_destination_table: String.t(),
  qualified_destination_trigger_fn: String.t(),
  qualified_insert_merge_deltas_fn: String.t(),
  qualified_source_table: String.t(),
  source_name: String.t() | nil,
  source_table_alias: String.t(),
  strategy: module(),
  strategy_data: map()
}
```

# `new`

```elixir
@spec new(config()) :: t()
```

Transforms a `t:config/0` configuration map into a `ProjectionData` struct. It resolves
qualified table names and the names of database objects managed by the engine.

---

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