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

Validates that Postgres foreign keys have supporting indexes.

## Examples

Before, the foreign key exists but the referencing column has no index:

```sql
CREATE TABLE accounts (
  id uuid PRIMARY KEY
);

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

Deletes or primary-key updates on `accounts` can become slow because Postgres
must scan `orders` to enforce the foreign key.

After, add an index whose leading columns are the foreign key columns:

```sql
CREATE INDEX orders_account_id_index ON orders (account_id);
```

Postgres can enforce the relationship with an index lookup instead of a table
scan.

## Notes

The supporting index does not have to be unique, and it may include extra
trailing columns such as `(account_id, inserted_at)`. Partial indexes such as
`WHERE deleted_at IS NULL` do not count as support for the foreign key.

## Options

By default the check inspects all non-system schemas in a Postgres target.
Use `schemas: [...]` or `tables: [...]` for simple filtering:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.MissingForeignKeyIndexes,
 schemas: ["public"],
 tables: ["orders", "line_items"]}
```

Use `rules: [...]` when the scope needs matchers or exclusions:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.MissingForeignKeyIndexes,
 rules: [
   [
     only: [schema: "public"],
     except: [[table: "audit_events"]]
   ]
 ]}
```

A foreign key passes when the referencing table has a valid, non-partial index
whose leading columns contain the foreign key columns.

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

# `check_opt`

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

# `check_opts`

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

# `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*
