# `Trellis.Migration`
[🔗](https://github.com/salesforce-misc/trellis/blob/v0.1.0-alpha/clients/elixir/lib/trellis/migration.ex#L2)

Trellis statements in an Ecto migration, so a transform is defined, and
dropped, the way the tables it reads are created.

    defmodule MyApp.Repo.Migrations.DefineWidgetPrices do
      use Ecto.Migration
      use Trellis.Migration

      # Required: see "Transactions" below.
      @disable_ddl_transaction true

      def up do
        create table(:widgets) do
          add :price, :integer
        end

        define "TRANSFORM widget_prices FROM widgets SELECT price AS price"
      end

      def down do
        apply "PAUSE TRANSFORM widget_prices"
        apply "DROP TRANSFORM widget_prices"
        drop table(:widgets)
      end
    end

`use Trellis.Migration` imports three helpers, each a thin call through
to the function of the same name on `Trellis`:

- `define/1`: a `TRANSFORM` statement, and nothing else (`Trellis.define/2`).
- `apply/1`: any statement of the grammar (`Trellis.apply/2`), such as the
  `PAUSE TRANSFORM` and `DROP TRANSFORM` a `down/0` undoes a define with.
  (`DROP` refuses a transform that isn't paused, and drops its target
  table.)
- `status/1`: the status of the transform writing a target, or `nil`
  (`Trellis.status/2`).

`define/1` and `apply/1` are queued like Ecto's own commands, so they run
in order with them: a transform can read a table created earlier in the
same migration. `status/1` runs when it's called, since a migration reads
it to decide what to queue. Each raises the `Trellis.Error` if the call
fails, which fails the migration.

## Transactions

Trellis doesn't join the migration's transaction. It runs every call on
connections of its own, and each call commits before it returns. If the
migration's transaction rolled back after a define, the host's changes
would be undone and the definition and its target table would not.

So a migration that uses these helpers has to turn its transaction off
with `@disable_ddl_transaction true`. `define/1` and `apply/1` check, as
they run, that the migration's repo has no transaction open, and raise
an `Ecto.MigrationError` before applying anything if it does. In a
migration that forgot the attribute, the migration's transaction then
rolls back with nothing applied on either side.

Without the transaction, a migration that fails partway leaves whatever
ran before the failure in place, on both sides. Give a migration that
defines transforms little else to do.

## `up/0` and `down/0`, not `change/0`

Ecto can reverse `create table` on its own, but not a Trellis statement:
the reverse of a define is a pause and a drop of a target that only the
statement names. So a module that uses `Trellis.Migration` must define
`up/0` and `down/0`, and defining `change/0` is a compile error, rather
than a rollback that fails partway.

## Which Trellis

Ecto runs migrations without starting the application, so the
application's supervised Trellis isn't running. Each helper connects a
handle of its own instead, from the `:trellis` key of the repo's
configuration, runs `Trellis.migrate/1` on it (creating Trellis's own
tables the first time), makes its one call, and shuts the handle down:

    # config/runtime.exs
    config :my_app, MyApp.Repo,
      url: database_url,
      trellis: [url: database_url]

The options are `Trellis.connect/1`'s. A migration's handle never runs
background work: `:staging` and `:drain_threads` are always the defaults,
whatever the configuration says.

## Running a migration once

A define isn't idempotent: a second define of a target that exists fails
with a `:conflict` error. Ecto's `schema_migrations` already records which
migrations have run, so a migration's define runs once per database, and
these helpers add no guard of their own.

A migration that has to tolerate a transform defined some other way (by
hand, or by a release task) can guard the define itself:

    def up do
      if status("widget_prices") == nil do
        define "TRANSFORM widget_prices FROM widgets SELECT price AS price"
      end
    end

The guard checks the target's name, not its definition: a changed
statement for a target that exists is an `ALTER TRANSFORM`.

# `apply`

```elixir
@spec apply(String.t()) :: :ok
```

Queues any statement of Trellis's grammar, to run in order with the
migration's other commands. It is `Trellis.apply/2`.

# `define`

```elixir
@spec define(String.t()) :: :ok
```

Queues a `TRANSFORM` statement, to run in order with the migration's
other commands. It is `Trellis.define/2`: any other statement form is
refused before anything is applied.

# `status`

```elixir
@spec status(String.t()) :: Trellis.Status.t() | nil
```

The status of the transform that writes `target_table`, or `nil` if none
does. It is `Trellis.status!/2`, run as it's called rather than queued.

---

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