Packages
ecto
0.6.0
3.14.1
3.14.0
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.1
3.13.0
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.2
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.4
3.8.3
3.8.2
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.1
3.0.0-rc.0
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.2.0-rc.1
2.2.0-rc.0
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.1.0-rc.5
2.1.0-rc.4
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.1.0-rc.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-beta.2
2.0.0-beta.1
2.0.0-beta.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.12.0-rc
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
A toolkit for data mapping and language integrated query for Elixir
Current section
Files
Jump to
Current section
Files
lib/ecto/migration/runner.ex
defmodule Ecto.Migration.Runner do
# A GenServer responsible for running migrations
# in either `:forward` or `:backward` directions.
@moduledoc false
use GenServer
require Logger
alias Ecto.Migration.Table
alias Ecto.Migration.Index
@doc """
Runs the given migration.
"""
def run(repo, module, direction, operation, opts) do
level = Keyword.get(opts, :level, :info)
start_link(repo, direction, level)
log(level, "== Running #{inspect module}.#{operation}/0 #{direction}")
{time, _} = :timer.tc(module, operation, [])
log(level, "== Migrated in #{inspect(div(time, 10000) / 10)}s")
stop()
end
@doc """
Starts the runner for the specified repo.
"""
def start_link(repo, direction, level) do
Agent.start_link(fn ->
%{direction: direction, repo: repo,
command: nil, subcommands: [], level: level}
end, name: __MODULE__)
end
@doc """
Stops the runner.
"""
def stop() do
Agent.stop(__MODULE__)
end
@doc """
Executes command tuples or strings.
Ecto.MigrationError will be raised when the server
is in `:backward` direction and `command` is irreversible.
"""
def execute(command) do
{repo, direction, level} = repo_and_direction_and_level()
execute_in_direction(repo, direction, level, command)
end
@doc """
Starts a command.
"""
def start_command(command) do
Agent.update __MODULE__, &put_in(&1.command, command)
end
@doc """
Executes and clears current command. Must call `start_command/1` first.
"""
def end_command do
command =
Agent.get_and_update __MODULE__, fn state ->
{operation, object} = state.command
{{operation, object, Enum.reverse(state.subcommands)},
%{state | command: nil, subcommands: []}}
end
execute(command)
end
@doc """
Adds a subcommand to the current command. Must call `start_command/1` first.
"""
def subcommand(subcommand) do
reply =
Agent.get_and_update(__MODULE__, fn
%{command: nil} = state ->
{:error, state}
state ->
{:ok, update_in(state.subcommands, &[subcommand|&1])}
end)
case reply do
:ok ->
:ok
:error ->
raise Ecto.MigrationError, message: "cannot execute command outside of block"
end
end
@doc """
Checks if a table or index exists.
"""
def exists?(object) do
{repo, direction, _level} = repo_and_direction_and_level()
exists = repo.adapter.ddl_exists?(repo, object)
if direction == :forward, do: exists, else: !exists
end
## Helpers
defp repo_and_direction_and_level do
Agent.get(__MODULE__, fn %{repo: repo, direction: direction, level: level} ->
{repo, direction, level}
end)
end
defp execute_in_direction(repo, :forward, level, command) do
log_ddl(level, command)
repo.adapter.execute_ddl(repo, command)
end
defp execute_in_direction(repo, :backward, level, command) do
reversed = reverse(command)
if reversed do
log_ddl(level, reversed)
repo.adapter.execute_ddl(repo, reversed)
else
raise Ecto.MigrationError, message: "cannot reverse migration command: #{inspect command}"
end
end
defp reverse([]), do: []
defp reverse([h|t]) do
if reversed = reverse(h) do
[reversed|reverse(t)]
end
end
defp reverse({:create, %Index{}=index}), do: {:drop, index}
defp reverse({:drop, %Index{}=index}), do: {:create, index}
defp reverse({:create, %Table{}=table, _columns}), do: {:drop, table}
defp reverse({:add, name, _type, _opts}), do: {:remove, name}
defp reverse({:alter, %Table{}=table, changes}) do
if reversed = reverse(changes) do
{:alter, table, reversed}
end
end
defp reverse(_), do: false
## Logging
defp log_ddl(level, ddl) when is_binary(ddl),
do: log(level, "execute #{inspect ddl}")
defp log_ddl(level, {:create, %Table{} = table, _}),
do: log(level, "create table #{table.name}")
defp log_ddl(level, {:alter, %Table{} = table, _}),
do: log(level, "alter table #{table.name}")
defp log_ddl(level, {:drop, %Table{} = table}),
do: log(level, "drop table #{table.name}")
defp log_ddl(level, {:create, %Index{} = index}),
do: log(level, "create index #{index.name}")
defp log_ddl(level, {:drop, %Index{} = index}),
do: log(level, "drop index #{index.name}")
defp log(:none, _msg), do: :ok
defp log(level, msg), do: Logger.log(level, msg)
end