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

Validates `Ecto.Changeset.check_constraint/3` annotations for Postgres checks.

## Examples

With a check constraint on `users.age`, before:

```elixir
def changeset(user, attrs) do
  user
  |> Ecto.Changeset.cast(attrs, [:age])
  |> Ecto.Changeset.validate_number(:age, greater_than_or_equal_to: 13)
end
```

The database still protects the invariant, but a constraint failure may reach
callers as a database error instead of a changeset error attached to `:age`.

After, annotate the changeset with the matching check constraint:

```elixir
def changeset(user, attrs) do
  user
  |> Ecto.Changeset.cast(attrs, [:age])
  |> Ecto.Changeset.validate_number(:age, greater_than_or_equal_to: 13)
  |> Ecto.Changeset.check_constraint(:age, name: :users_age_check)
end
```

Ecto can turn the database rejection into a normal changeset error.

## Notes

The check skips dynamic `cast` or `change` field lists, check expressions
without catalog column metadata, and constraints whose columns cannot be
mapped to Ecto schema fields.

## Options

The check discovers compiled Ecto schemas through reflection, parses source
files for conservative changeset candidates, and only requires
`check_constraint/3` when Postgres can associate a check constraint with
fields that a candidate casts. Dynamic cast/change field lists and check
expressions without catalog column metadata are skipped for v1.

Pass `paths: [...]` so Bylaw can parse source AST for user-defined changeset
functions:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.EctoChangesetCheckConstraints,
 paths: ["lib/my_app"]}
```

When the repo can report `config()[:otp_app]`, schema module discovery is
derived from it. Use `schema_modules: [...]` when the check should inspect an
explicit set of schemas instead:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.EctoChangesetCheckConstraints,
 paths: ["lib/my_app/catalog"],
 schema_modules: [MyApp.Catalog.Product, MyApp.Catalog.Price]}
```

Use `rules: [...]` to scope the Postgres constraints considered by the check:

```elixir
{Bylaw.Db.Adapters.Postgres.Checks.EctoChangesetCheckConstraints,
 paths: ["lib/my_app"],
 rules: [
   [
     only: [schema: "public"],
     except: [[table: "legacy_products", constraint: "legacy_price_check"]]
   ]
 ]}
```

## 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()}
  | {:otp_app, atom()}
  | {:paths, [Path.t()]}
  | {:schema_modules, [module()]}
  | {: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*
