# `Bylaw.Ecto.Query.Checks.MandatoryJoinKeys`
[🔗](https://github.com/ryanzidago/bylaw/blob/v0.1.0-alpha.1/lib/bylaw/ecto/query/checks/mandatory_join_keys.ex#L1)

Validates that explicit schema joins preserve configured mandatory keys.

This check is intentionally narrow. It handles direct schema joins.

## Examples

Bad:

    from(Post, as: :post)
    |> join(:inner, [post: p], c in Comment,
      as: :comment,
      on: c.post_id == p.id
    )

Why this is bad:

If `:organization_id` is configured as mandatory, the join preserves the row
relationship but not the tenant key. Bad or inconsistent foreign-key data can
cross tenant boundaries.

Better:

    from(Post, as: :post)
    |> join(:inner, [post: p], c in Comment,
      as: :comment,
      on:
        c.post_id == p.id and
          c.organization_id == p.organization_id
    )

Why this is better:

The join requires both the row relationship and the configured key to match.

## Notes

This check only validates direct explicit schema joins and supported equality
predicates in the join `on` expression. Association joins, subqueries,
fragments, and schema-less joins are ignored.

Association joins, subqueries, fragments, and schema-less joins are not
validated by this check.

Like Bylaw's other Ecto query checks, this reads Ecto query structs directly.
Ecto treats those structs as opaque, so this check intentionally supports a
small, tested subset of Ecto's query AST.

## Options

  * `:validate` - explicit `false` disables the check. Defaults to `true`.
  * `:keys` - required non-empty list of field names when the check runs.
  * `:match` - `:any` or `:all`. Defaults to `:any`.

Example check spec:

    {Bylaw.Ecto.Query.Checks.MandatoryJoinKeys,
     keys: [:organization_id],
     match: :all}

When a join schema does not contain any configured keys, that join is not
applicable and the check returns no issue for it. For applicable joins, the
check accepts direct equality predicates between the joined binding and
another query binding in the join `on` expression.

## Usage

Add this module to the explicit check list passed through `Bylaw.Ecto.Query`.
See `Bylaw.Ecto.Query` for the full `c:Ecto.Repo.prepare_query/3` setup.

# `validate`

```elixir
@spec validate(
  Bylaw.Ecto.Query.Check.operation(),
  Bylaw.Ecto.Query.Check.query(),
  opts()
) ::
  Bylaw.Ecto.Query.Check.result()
```

Implements the `Bylaw.Ecto.Query.Check` validation callback.

---

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