mirror of
https://github.com/valitydev/psql-migration.git
synced 2024-11-06 01:05:18 +00:00
Add reset and setup
This commit is contained in:
parent
651b622773
commit
91c48c985b
@ -1,7 +1,7 @@
|
||||
-module(psql_migration).
|
||||
|
||||
%% API exports
|
||||
-export([main/1, open_connection/0, open_connection/1]).
|
||||
-export([main/1, open_connection/1, connection_opts/1]).
|
||||
|
||||
%%====================================================================
|
||||
%% API functions
|
||||
@ -25,7 +25,9 @@ usage(ExitCode) ->
|
||||
{"new <name>", "Create a new migration"},
|
||||
{"list", "List migrations indicating which have been applied"},
|
||||
{"run", "Run all migrations"},
|
||||
{"revert", "Revert the last migration"}
|
||||
{"revert", "Revert the last migration"},
|
||||
{"reset", "Resets your database by dropping the database in your DATABASE_URL and then runs `setup`"},
|
||||
{"setup", "Creates the database specified in your DATABASE_URL, and runs any existing migrations."}
|
||||
]),
|
||||
halt(ExitCode).
|
||||
|
||||
@ -57,41 +59,63 @@ handle_command({ok, {Args, ["new", Name]}}) ->
|
||||
"-- Down migration\n"
|
||||
],
|
||||
file:write_file(Filename, list_to_binary(C), [exclusive]),
|
||||
io:format("Created migration: ~s~n", [Filename]);
|
||||
handle_command_result({ok, "Created migration: ~s~n", [Filename]});
|
||||
handle_command({ok, {Args, ["run"]}}) ->
|
||||
Available = available_migrations(Args),
|
||||
case open_connection(Args) of
|
||||
{ok, Conn} ->
|
||||
Applied = applied_migrations(Conn),
|
||||
ToApply = lists:filter(fun ({Mig, _}) -> not lists:member(Mig, Applied) end, Available),
|
||||
Results = apply_migrations(up, ToApply, Conn),
|
||||
report_migrations(up, Results);
|
||||
{error, Error} ->
|
||||
io:format("Failed to connect to database: ~p~n", [Error]),
|
||||
halt(1)
|
||||
end;
|
||||
Result = with_connection(Args,
|
||||
fun(Conn) ->
|
||||
Applied = applied_migrations(Conn),
|
||||
ToApply = lists:filter(fun ({Mig, _}) -> not lists:member(Mig, Applied) end, Available),
|
||||
Results = apply_migrations(up, ToApply, Conn),
|
||||
report_migrations(up, Results)
|
||||
end),
|
||||
handle_command_result(Result);
|
||||
handle_command({ok, {Args, ["revert"]}}) ->
|
||||
Available = available_migrations(Args),
|
||||
case open_connection(Args) of
|
||||
{ok, Conn} ->
|
||||
case applied_migrations(Conn) of
|
||||
[] ->
|
||||
io:format("No applied migrations to revert"),
|
||||
halt(1);
|
||||
Applied ->
|
||||
LastApplied = lists:last(Applied),
|
||||
case lists:keyfind(LastApplied, 1, Available) of
|
||||
false ->
|
||||
io:format("Migration ~p can not be found~n", [LastApplied]),
|
||||
halt(1);
|
||||
Migration ->
|
||||
Results = apply_migrations(down, [Migration], Conn),
|
||||
report_migrations(down, Results)
|
||||
end
|
||||
end;
|
||||
{error, Error} ->
|
||||
io:format("Failed to connect to database: ~p~n", [Error]),
|
||||
halt(1)
|
||||
Result = with_connection(Args,
|
||||
fun(Conn) ->
|
||||
case applied_migrations(Conn) of
|
||||
[] ->
|
||||
{error, "No applied migrations to revert"};
|
||||
Applied ->
|
||||
LastApplied = lists:last(Applied),
|
||||
case lists:keyfind(LastApplied, 1, Available) of
|
||||
false ->
|
||||
{error, "Migration ~p can not be found~n", [LastApplied]};
|
||||
Migration ->
|
||||
Results = apply_migrations(down, [Migration], Conn),
|
||||
report_migrations(down, Results)
|
||||
end
|
||||
end
|
||||
end),
|
||||
handle_command_result(Result);
|
||||
handle_command({ok, {Args, ["reset"]}}) ->
|
||||
{ok, Opts} = connection_opts(Args),
|
||||
case maps:take(database, Opts) of
|
||||
error ->
|
||||
handle_command_result({error, "No database to reset~n"});
|
||||
{Database, Opts1} ->
|
||||
ok = with_connection(Opts1,
|
||||
fun(Conn) ->
|
||||
if_ok(epgsql:squery(Conn, "drop database if exists " ++ Database))
|
||||
end),
|
||||
handle_command({ok, {Args, ["setup"]}})
|
||||
end;
|
||||
handle_command({ok, {Args, ["setup"]}}) ->
|
||||
{ok, Opts} = connection_opts(Args),
|
||||
case maps:take(database, Opts) of
|
||||
error ->
|
||||
handle_command_result({error, "No database to reset~n"});
|
||||
{Database, Opts1} ->
|
||||
case with_connection(Opts1,
|
||||
fun(Conn) ->
|
||||
if_ok(epgsql:squery(Conn, "create database " ++ Database))
|
||||
end) of
|
||||
ok ->
|
||||
handle_command({ok, {Args, ["run"]}});
|
||||
Other ->
|
||||
handle_command_result(Other)
|
||||
end
|
||||
end;
|
||||
handle_command({ok, {_, _}}) ->
|
||||
usage(1).
|
||||
@ -100,8 +124,36 @@ handle_command({ok, {_, _}}) ->
|
||||
|
||||
%% Utils
|
||||
|
||||
-spec open_connection() -> {ok, epgsql:connection()} | {error, term()}.
|
||||
open_connection() ->
|
||||
|
||||
-type command_result() ::ok |
|
||||
{ok, io:format(), [term()]} |
|
||||
{error, string()} |
|
||||
{error, io:format(), [term()]}.
|
||||
|
||||
-spec handle_command_result(command_result()) -> no_return().
|
||||
handle_command_result(ok) ->
|
||||
halt(0);
|
||||
handle_command_result({ok, Fmt, Args}) ->
|
||||
io:format(Fmt, Args),
|
||||
halt(0);
|
||||
handle_command_result({error, Str}) ->
|
||||
handle_command_result({error, Str, []});
|
||||
handle_command_result({error, Fmt, Args}) ->
|
||||
io:format(Fmt, Args),
|
||||
halt(1).
|
||||
|
||||
|
||||
-spec with_connection(list() | map(), fun((epgsql:connnection()) -> command_result())) -> command_result().
|
||||
with_connection(Args, Fun) ->
|
||||
case open_connection(Args) of
|
||||
{ok, Conn} ->
|
||||
Fun(Conn);
|
||||
{error, Error} ->
|
||||
{error, "Failed to connect to database: ~p~n", [Error]}
|
||||
end.
|
||||
|
||||
connection_opts(Args) ->
|
||||
envloader:load(dot_env(Args)),
|
||||
URL = os:getenv("DATABASE_URL"),
|
||||
ParseOpts = [{scheme_defaults, [{postgres, 5432}, {postgresql, 5432}]}],
|
||||
case http_uri:parse(URL, ParseOpts) of
|
||||
@ -114,17 +166,20 @@ open_connection() ->
|
||||
[[], []] -> {"postgres", ""};
|
||||
[U, P] -> {U, P}
|
||||
end,
|
||||
OpenOpts = #{ port => Port,
|
||||
username => User,
|
||||
password => Pass,
|
||||
host =>Host,
|
||||
database => string:slice(Database, 1)},
|
||||
epgsql:connect(OpenOpts)
|
||||
{ok, #{ port => Port,
|
||||
username => User,
|
||||
password => Pass,
|
||||
host =>Host,
|
||||
database => string:slice(Database, 1)}}
|
||||
end.
|
||||
|
||||
open_connection(Args) ->
|
||||
envloader:load(dot_env(Args)),
|
||||
open_connection().
|
||||
|
||||
-spec open_connection(list() | map()) -> {ok, epgsql:connection()} | {error, term()}.
|
||||
open_connection(Args) when is_list(Args) ->
|
||||
{ok, Opts} = connection_opts(Args),
|
||||
open_connection(Opts);
|
||||
open_connection(Opts) ->
|
||||
epgsql:connect(Opts).
|
||||
|
||||
target_dir(Args) ->
|
||||
case lists:keyfind(dir, 1, Args) of
|
||||
@ -144,9 +199,11 @@ dot_env(Args) ->
|
||||
report_migrations(_, L) when length(L) == 0 ->
|
||||
io:format("No migrations were run~n");
|
||||
report_migrations(up, Results) ->
|
||||
[io:format("Applied ~s: ~p~n", [V, R]) || {V, R} <- Results];
|
||||
[io:format("Applied ~s: ~p~n", [V, R]) || {V, R} <- Results],
|
||||
ok;
|
||||
report_migrations(down, Results) ->
|
||||
[io:format("Reverted ~s: ~p~n", [V, R]) || {V, R} <- Results].
|
||||
[io:format("Reverted ~s: ~p~n", [V, R]) || {V, R} <- Results],
|
||||
ok.
|
||||
|
||||
-define(DRIVER, epgsql).
|
||||
|
||||
@ -188,7 +245,7 @@ if_ok(Rs) when is_list(Rs) ->
|
||||
if_ok({ok, _}) -> ok;
|
||||
if_ok({ok, _, _}) -> ok;
|
||||
if_ok({ok, _, _, _}) -> ok;
|
||||
if_ok({error, {error,error,_,_,Descr,_}}) -> {error, Descr};
|
||||
if_ok({error, {error,error,_,_,Descr,_}}) -> {error, binary_to_list(Descr)};
|
||||
if_ok(Error) -> {error, Error}.
|
||||
|
||||
available_migrations(Args) ->
|
||||
@ -200,13 +257,11 @@ available_migrations(Args) ->
|
||||
end, lists:usort(Files)).
|
||||
|
||||
applied_migrations(Args) when is_list(Args) ->
|
||||
case open_connection(Args) of
|
||||
{ok, Conn} ->
|
||||
applied_migrations(Conn);
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
end;
|
||||
applied_migrations(Conn) ->
|
||||
with_connection(Args,
|
||||
fun(Conn) ->
|
||||
applied_migrations(Conn)
|
||||
end);
|
||||
applied_migrations(Conn) when is_pid(Conn) ->
|
||||
case ?DRIVER:squery(Conn, "SELECT id FROM __migrations ORDER by id ASC") of
|
||||
{ok, _, Migs} -> [binary_to_list(Mig) || {Mig} <- Migs];
|
||||
{error, {error, error, <<"42P01">>, _, _, _}} ->
|
||||
|
Loading…
Reference in New Issue
Block a user