# Repo Module Setup

## Basic usage

Simply add `use Lazarus` to your repo module:

```elixir
defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres
  use Lazarus
end
```

That does three things:

- Blocks direct `Repo.delete*` and `Repo.delete_all*` calls unless the schema or
  table is bypassed
- Adds explicit `Repo.soft_delete*` and `Repo.hard_delete*` functions
- Hides soft-deleted rows from normal reads and skips them in updates by default

## Bypassing schemas and tables

If your repo absolutely NEEDS to leave specific schemas/tables alone, you can
whitelist schema modules and table names by passing in a list of schemas as
`bypass_schemas` and a list of table names as `bypass_tables`.

Bypassed sources are left alone by Lazarus. Reads, updates, and deletes against
them use ordinary Ecto behavior including `Repo.delete`, and `Repo.delete_all`
where those operations apply. These options were designed with third party
modules/libraries in mind that run queries, updates, and deletes on your repo
(e.g. when Oban reads from, updates, or deletes jobs from the `oban_jobs`
table).

When a bypassed source is the root of an Ecto query, Lazarus leaves that query
alone, including fragments inside it. A bypassed join source is allowed, but it
does not make unrelated fragments elsewhere in the query safe.

Bypasses do not apply to direct raw SQL calls such as `Repo.query!/3` because
Lazarus cannot inspect contents of raw SQL queries. See
[Overcoming Limitations](overcoming-limitations.md#raw-sql) for more info.

### Example

```elixir
use Lazarus,
  bypass_schemas: [SessionToken],
  bypass_tables: ["audit_logs"]
```

### Which option should you use?

- `bypass_schemas` whitelists **schemas and their source tables**
- `bypass_tables` whitelists **tables only** (useful for schema-less tables)

If you have a schema module `SessionToken` with a table `"session_tokens"`,
bypassing just the schema module is sufficient enough, e.g.
`bypass_schemas: [SessionToken]`. Adding the table name to `bypass_tables` is
redundant.

If your database has a table and no schema for it, you can bypass it by name,
e.g. `bypass_tables: ["audit_logs"]`.

If you have a schema for your table, and you decide to only bypass the table
name and not the schema module itself (for whatever reason), you might end up
with some inconsistent bypass results:

```elixir
# Success (query operations)
Repo.update_all(
  from(t in SessionToken, where: t.expires_at < ^DateTime.utc_now()),
  set: [expired: true]
)

Repo.update_all(
  from(t in "session_tokens", where: field(t, :expires_at) < ^DateTime.utc_now()),
  set: [expired: true]
)

Repo.delete_all(from t in SessionToken, where: t.expires_at < ^DateTime.utc_now())
Repo.delete_all(from t in "session_tokens", where: field(t, :expires_at) < ^DateTime.utc_now())

# Raises (schemas, changesets)
Repo.delete(session_token)
Repo.delete(session_token_changeset)
Repo.delete_all(SessionToken)
```

### Real-world example with Oban

Bypasses `Oban.Job` schema with `"oban_jobs"` table, and a schema-less
`"oban_peers"` table

```elixir
use Lazarus,
  bypass_schemas: [Oban.Job],
  bypass_tables: ["oban_peers"]
```
