# `Lotus.Preflight`
[🔗](https://github.com/elixir-lotus/lotus/blob/v1.1.0/lib/lotus/preflight.ex#L1)

Statement preflight authorization for Lotus.

Delegates to the adapter's `extract_accessed_resources/2` callback to
discover which tables/relations a statement will access, then checks
those relations against visibility rules before execution.

This provides defense-in-depth by blocking queries that would access
denied tables, even if they're accessed through views or complex subqueries.

When an adapter returns `{:unrestricted, reason}` the statement is allowed
through only if the host application has opted in via
`config :lotus, :allow_unrestricted_resources`; otherwise preflight returns
an error. The opted-in case records `{:unrestricted, reason}` rather than a
relation list, so a later consumer can tell "touches no table" apart from
"this adapter cannot name its tables".

# `analyze`

```elixir
@spec analyze(
  Lotus.Source.Adapter.t(),
  Lotus.Query.Statement.t(),
  String.t() | nil,
  term()
) ::
  {:ok, Lotus.Preflight.Relations.outcome()} | {:error, String.t()}
```

Authorizes a statement and returns what preflight learned about it.

The same check as `authorize/4`, returning the outcome as a value: the list
of `{schema, table}` relations the statement touches — empty when it touches
none — or `{:unrestricted, reason}` when the adapter cannot name them and the
host opted in. `Lotus.Runner` carries this value down the pipeline and into
the `:before_execute` and `:after_query` payloads.

# `authorize`

```elixir
@spec authorize(
  Lotus.Source.Adapter.t(),
  Lotus.Query.Statement.t(),
  String.t() | nil,
  term()
) ::
  :ok | {:error, String.t()}
```

Authorizes a statement by checking all relations it would access.

Delegates resource extraction to the adapter, then validates each
relation against the visibility rules.

`scope` is the opaque caller-supplied value handed to the visibility
resolver, the same one `Lotus.list_tables/2` and friends accept. Pass it
and a per-scope deny blocks execution; omit it and only the unscoped
rules apply. Without it a resolver that hides a table from one tenant
would hide it in the explorer while the query still returned its rows.

---

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