defmodule Literature.Migrations.Postgres do
@moduledoc """
update @current_version if adding new migration
"""
@behaviour Literature.Migration
use Ecto.Migration
@initial_version 1
@current_version 3
@default_prefix "public"
@doc false
def initial_version, do: @initial_version
@doc false
def current_version, do: @current_version
@impl Literature.Migration
def up(opts) do
opts = with_defaults(opts, @current_version)
initial = migrated_version(opts)
cond do
initial == 0 ->
change(@initial_version..opts.version, :up, opts)
initial < opts.version ->
change((initial + 1)..opts.version, :up, opts)
true ->
:ok
end
end
@impl Literature.Migration
def down(opts) do
opts = with_defaults(opts, @initial_version)
initial = max(migrated_version(opts), @initial_version)
if initial >= opts.version do
change(initial..opts.version, :down, opts)
end
end
@impl Literature.Migration
def migrated_version(opts) do
opts = with_defaults(opts, @initial_version)
repo = Map.get_lazy(opts, :repo, fn -> repo() end)
escaped_prefix = Map.fetch!(opts, :escaped_prefix)
query = """
SELECT description
FROM pg_class
LEFT JOIN pg_description ON pg_description.objoid = pg_class.oid
LEFT JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE pg_class.relname = 'literature_publications'
AND pg_namespace.nspname = '#{escaped_prefix}'
"""
case repo.query(query, [], log: false) do
{:ok, %{rows: [[version]]}} when is_binary(version) -> String.to_integer(version)
_ -> 0
end
end
defp change(range, direction, opts) do
for index <- range do
pad_idx = String.pad_leading(to_string(index), 2, "0")
[__MODULE__, "V#{pad_idx}"]
|> Module.concat()
|> apply(direction, [opts])
end
case direction do
:up -> record_version(opts, Enum.max(range))
:down -> record_version(opts, Enum.min(range) - 1)
end
end
defp record_version(_opts, 0), do: :ok
defp record_version(%{prefix: prefix}, version) do
execute("COMMENT ON TABLE #{inspect(prefix)}.literature_publications IS '#{version}'")
end
defp with_defaults(opts, version) do
opts = Enum.into(opts, %{prefix: @default_prefix, version: version})
opts
|> Map.put_new(:create_schema, opts.prefix != @default_prefix)
|> Map.put_new(:quoted_prefix, inspect(opts.prefix))
|> Map.put_new(:escaped_prefix, String.replace(opts.prefix, "'", "\\'"))
end
end