# `Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions`
[🔗](https://github.com/ryanzidago/bylaw/blob/v0.1.0-alpha.1/lib/bylaw/db/adapters/postgres/checks/foreign_key_actions.ex#L1)

Validates Postgres foreign key `ON DELETE` and `ON UPDATE` actions.

## Examples

With this rule:

```elixir
[only: [referenced_table: "accounts"], on_delete: :restrict]
```

Before, the foreign key deletes orders when an account is deleted:

```sql
CREATE TABLE orders (
  id uuid PRIMARY KEY,
  account_id uuid NOT NULL REFERENCES accounts(id) ON DELETE CASCADE
);
```

That may silently remove business records that should survive account cleanup
or require an explicit archival flow.

After, use the action required by the rule:

```sql
CREATE TABLE orders (
  id uuid PRIMARY KEY,
  account_id uuid NOT NULL REFERENCES accounts(id) ON DELETE RESTRICT
);
```

Postgres now blocks accidental parent deletion until the application handles
dependent rows intentionally.

## Notes

This check only validates actions you configure. If a rule only sets
`on_delete`, the `ON UPDATE` action is ignored for that rule.

## Options

Use a rule without `:only` when every foreign key in scope should use the same
action:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions,
 rules: [[on_delete: :cascade]]}
```

Use `rules: [...]` for scoped policy. A foreign key can match more than one
rule, and matching rules accumulate.

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions,
 rules: [
   [
     only: [[table: "messages"], [referenced_table: "conversations"]],
     on_delete: :cascade
   ],
   [
     only: [referenced_table: "lookup_statuses"],
     on_delete: :restrict,
     on_update: :restrict
   ]
 ]}
```

## Usage

Add this module to the checks passed to
`Bylaw.Db.Adapters.Postgres.validate/2`. See the
[README usage section](readme.html#usage) for the full ExUnit setup.

# `action`

```elixir
@type action() :: :no_action | :restrict | :cascade | :set_null | :set_default
```

# `check_opt`

```elixir
@type check_opt() :: {:validate, boolean()} | {:rules, [rule()]}
```

# `check_opts`

```elixir
@type check_opts() :: [check_opt()]
```

# `matcher`

```elixir
@type matcher() :: [
  schema: matcher_values(),
  table: matcher_values(),
  constraint: matcher_values(),
  column: matcher_values(),
  referenced_schema: matcher_values(),
  referenced_table: matcher_values(),
  referenced_column: matcher_values()
]
```

# `matcher_value`

```elixir
@type matcher_value() :: String.t() | Regex.t()
```

# `matcher_values`

```elixir
@type matcher_values() :: matcher_value() | [matcher_value()]
```

# `normalized_rule`

```elixir
@type normalized_rule() :: %{
  only: [matcher()],
  except: [matcher()],
  on_delete: action() | nil,
  on_update: action() | nil
}
```

# `rule`

```elixir
@type rule() :: [
  only: matcher() | [matcher()],
  except: matcher() | [matcher()],
  on_delete: action(),
  on_update: action()
]
```

# `validate`

```elixir
@spec validate(target :: Bylaw.Db.Target.t(), opts :: check_opts()) ::
  Bylaw.Db.Check.result()
```

Implements the `Bylaw.Db.Check` validation callback.

---

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