Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions (bylaw_postgres v0.2.0)

Copy Markdown View Source

Validates Postgres foreign key ON DELETE and ON UPDATE actions.

Examples

With this rule:

[where: [referenced_tables: ["accounts"]], on_delete: :restrict]

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

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:

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

  • :validate - explicit false disables this check.
  • :rules - rule keyword list or non-empty list of rule keyword lists.
  • :on_delete - expected foreign key ON DELETE action inside each rule.
  • :on_update - expected foreign key ON UPDATE action inside each rule.

This check requires at least one of :on_delete or :on_update, so bare-module configuration is not valid.

Run globally:

{Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions,
 rules: [on_delete: :restrict, on_update: :restrict]}

Run only for matching rule scopes:

{Bylaw.Db.Adapters.Postgres.Checks.ForeignKeyActions,
 rules: [
   [
     where: [referenced_tables: ["lookup_statuses"]],
     on_delete: :restrict,
     on_update: :restrict
   ],
   [
     where: [tables: ["messages"]],
     except: [constraints: ["messages_status_id_fkey"]],
     on_delete: :cascade
   ]
 ]}

A foreign key can match more than one rule, and matching rules accumulate.

Usage

Add this module to the checks passed to Bylaw.Db.Adapters.Postgres.validate/2. See the README usage section for the full ExUnit setup.

Summary

Functions

Implements the Bylaw.Db.Check validation callback.

Types

action()

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

check_opt()

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

check_opts()

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

matcher()

@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()

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

matcher_values()

@type matcher_values() :: [matcher_value()]

normalized_rule()

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

rule()

@type rule() :: [
  where: matcher() | [matcher()],
  except: matcher() | [matcher()],
  on_delete: action(),
  on_update: action()
]

Functions

validate(target, opts)

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

Implements the Bylaw.Db.Check validation callback.