1
0
mirror of https://github.com/UzixLS/migresia.git synced 2025-07-19 07:11:28 +03:00

Add rollback_last/0, fix #2 Don't rollback migrations already rolled back

This commit is contained in:
Grzegorz Junka
2016-01-08 19:04:39 +00:00
parent 7488a94837
commit 971a02b321
4 changed files with 97 additions and 57 deletions

View File

@ -27,7 +27,7 @@
{application, migresia,
[
{description, "Simple mnesia database migration tool"},
{vsn, "0.0.3"},
{vsn, "0.0.4"},
{modules, [migresia, migresia_migrations]},
{registered, []},
{applications, [kernel, stdlib]}

View File

@ -1,11 +1,19 @@
%% -*- mode: erlang -*-
{"0.0.4",
[
{"0.0.3",
[{"0.0.2",
[{load_module, migresia_migrations},
{load_module, migresia}
]}],
[{"0.0.2",
{load_module, migresia}]},
{"0.0.2",
[{load_module, migresia_migrations},
{load_module, migresia}
]}]
{load_module, migresia}]}
],
[
{"0.0.3",
[{load_module, migresia_migrations},
{load_module, migresia}]},
{"0.0.2",
[{load_module, migresia_migrations},
{load_module, migresia}]}
]
}.

View File

@ -25,13 +25,16 @@
-module(migresia).
-export([start_all_mnesia/0,
list_nodes/0,
list_migrations/0,
ensure_started/1,
check/1,
migrate/0,
migrate/1,
rollback/1,
rollback/2,
list_nodes/0]).
rollback_last/0,
rollback_last/1]).
%%------------------------------------------------------------------------------
@ -49,6 +52,9 @@ start_all_mnesia() ->
list_nodes() ->
mnesia:table_info(schema, disc_copies).
list_migrations() ->
migresia_migrations:list_migrations().
ensure_started_on_remotes(Nodes) ->
io:format("Ensuring Mnesia is running on nodes:~n~p~n", [Nodes]),
{ResL, BadNodes} = rpc:multicall(Nodes, migresia, ensure_started, [mnesia]),
@ -131,14 +137,21 @@ rollback(Srcs, Time) ->
rollback1(Srcs, Time) ->
io:format("Waiting for tables (max timeout 2 minutes)...~n", []),
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
case migresia_migrations:list_all_ups(Srcs) of
case migresia_migrations:list_applied_ups(Srcs, Time) of
{error, _} = Err -> Err;
Ups -> apply_downs(Srcs, Ups, Time)
end.
apply_downs(Srcs, Ups, Time) ->
apply_downs(Srcs, Loaded, Time) ->
%% Load the transform function on all nodes, see:
%% http://toddhalfpenny.com/2012/05/21/possible-erlang-bad-transform-function-solution/
rpc:multicall(nodes(), migresia_migrations, list_all_ups, [Srcs]),
ToRollBack = lists:reverse([X || {_, Ts} = X <- Ups, Ts > Time]),
lists:foreach(fun migresia_migrations:execute_down/1, ToRollBack).
rpc:multicall(nodes(), migresia_migrations, list_applied_ups, [Srcs, Time]),
lists:foreach(fun migresia_migrations:execute_down/1, Loaded).
%%------------------------------------------------------------------------------
-spec rollback_last() -> ok | {error, any()}.
rollback_last() -> rollback(migresia_migrations:get_ts_before_last()).
-spec rollback_last(migration_sources()) -> ok | {error, any()}.
rollback_last(Srcs) -> rollback(Srcs, migresia_migrations:get_ts_before_last()).

View File

@ -25,15 +25,19 @@
-module(migresia_migrations).
-export([init_migrations/0,
list_migrations/0,
list_unapplied_ups/1,
list_all_ups/1,
list_applied_ups/2,
get_default_dir/0,
get_priv_dir/1,
get_ts_before_last/0,
execute_up/1,
execute_down/1]).
-define(DIR, <<"migrations">>).
-define(TABLE, schema_migrations).
%% Surely no migrations before the first commit in migresia
-define(FIRST_TS, 20130404041545).
-type mod_bin_list() :: [{module(), binary()}].
@ -53,24 +57,27 @@ init_migrations() ->
end
end.
list_migrations() ->
mnesia:dirty_all_keys(?TABLE).
%%------------------------------------------------------------------------------
-spec list_unapplied_ups(migresia:migration_sources())
-> mod_bin_list().
-spec list_unapplied_ups(migresia:migration_sources()) -> mod_bin_list().
list_unapplied_ups({rel_relative_dir, default}) ->
list_unapplied_ups({rel_relative_dir, get_default_dir()});
list_unapplied_ups({rel_relative_dir, DirName}) ->
get_migrations(get_release_dir(DirName));
get_unapplied(get_release_dir(DirName));
list_unapplied_ups(App) when is_atom(App) ->
get_migrations(get_priv_dir(App)).
get_unapplied(get_priv_dir(App)).
-spec list_all_ups(migresia:migration_sources()) -> mod_bin_list().
list_all_ups({rel_relative_dir, default}) ->
list_all_ups({rel_relative_dir, get_default_dir()});
list_all_ups({rel_relative_dir, DirName}) ->
get_all_migrations(get_release_dir(DirName));
list_all_ups(App) when is_atom(App) ->
get_all_migrations(get_priv_dir(App)).
-spec list_applied_ups(migresia:migration_sources(), integer()) ->
mod_bin_list().
list_applied_ups({rel_relative_dir, default}, Time) ->
list_applied_ups({rel_relative_dir, get_default_dir()}, Time);
list_applied_ups({rel_relative_dir, DirName}, Time) ->
get_applied(get_release_dir(DirName), Time);
list_applied_ups(App, Time) when is_atom(App) ->
get_applied(get_priv_dir(App), Time).
get_default_dir() ->
case application:get_env(migresia, rel_relative_dir) of
@ -111,23 +118,37 @@ check_priv_dir(App) ->
false -> throw({error, enoent})
end.
-spec get_migrations(binary()) -> mod_bin_list().
get_migrations(Dir) ->
ToApply = check_dir(file:list_dir(Dir)),
-spec get_unapplied(binary()) -> mod_bin_list().
get_unapplied(Dir) ->
load_migrations(Dir, fun list_unapplied/2).
-spec get_applied(binary(), integer()) -> mod_bin_list().
get_applied(Dir, Time) ->
load_migrations(Dir, fun(X, Y) -> list_applied(X, Y, Time) end).
list_unapplied(FromDir, FromDB) ->
Unapplied = [X || {Ts, _} = X <- FromDir,
length(FromDB) =:= 0 orelse Ts > lists:max(FromDB)],
lists:keysort(1, Unapplied).
list_applied(FromDir, FromDB, Time) ->
Applied = [X || {Ts, _} = X <- FromDir,
lists:member(Ts, FromDB), Ts > Time],
lists:reverse(lists:keysort(1, Applied)).
load_migrations(Dir, FilterFun) ->
Migrations = check_dir(file:list_dir(Dir)),
case check_table() of
{error, _} = Err -> throw(Err);
Applied -> compile_and_load(Dir, ToApply, Applied)
Applied -> compile_and_load(Dir, FilterFun, Migrations, Applied)
end.
get_all_migrations(Dir) ->
ToApply = lists:sort(check_dir(file:list_dir(Dir))),
compile_and_load(Dir, ToApply, []).
check_dir({error, _} = Err) -> throw(Err);
check_dir({ok, Filenames}) -> normalize_names(Filenames, []).
normalize_names([<<Short:14/bytes, ".erl">>|T], Acc) ->
normalize_names(T, [{Short, Short}|Acc]);
Int = list_to_integer(binary_to_list(Short)),
normalize_names(T, [{Int, Short}|Acc]);
normalize_names([<<Short:14/bytes, $_, R/binary>> = Name|T], Acc)
when size(R) >= 4
andalso erlang:binary_part(R, size(R) - 4, 4) == <<".erl">> ->
@ -159,30 +180,16 @@ check_table() ->
end
end.
compile_and_load(Dir, ToApply, Applied) ->
ToExecute = compile_unapplied(Dir, ToApply, Applied, []),
Fun = fun({Module, Short, Binary}) ->
load_migration(Module, Short, Binary)
end,
lists:map(Fun, ToExecute).
compile_and_load(Dir, FilterFun, ToApply, Applied) ->
ToLoad = FilterFun(ToApply, Applied),
lists:map(fun(X) -> compile_and_load1(Dir, X) end, ToLoad).
compile_and_load1(Dir, {Short, Name}) ->
{Module, Short, Binary} = compile_file(Dir, Short, Name),
load_migration(Module, Short, Binary).
%%------------------------------------------------------------------------------
compile_unapplied(Dir, [{Short, Module}|TN], [] = Old, Acc) ->
compile_unapplied(Dir, TN, Old, [compile_file(Dir, Short, Module)|Acc]);
compile_unapplied(Dir, [{Last, _}|TN], [Last], Acc) ->
compile_unapplied(Dir, TN, [], Acc);
compile_unapplied(Dir, [{Last, _}], [Last|_TO], Acc) ->
compile_unapplied(Dir, [], [], Acc);
compile_unapplied(Dir, [{Short, _}|TN], [Short|TO], Acc) ->
compile_unapplied(Dir, TN, TO, Acc);
compile_unapplied(Dir, [{New, _}|TN], [Old|_] = Applied, Acc) when New < Old ->
compile_unapplied(Dir, TN, Applied, Acc);
compile_unapplied(Dir, [{New, _}|_] = ToApply, [Old|TO], Acc) when New > Old ->
compile_unapplied(Dir, ToApply, TO, Acc);
compile_unapplied(_Dir, [], _, Acc) ->
lists:reverse(Acc).
compile_file(Dir, Short, Name) ->
File = filename:join(Dir, Name),
io:format("Compiling: ~s~n", [File]),
@ -196,7 +203,7 @@ compile_file(Dir, Short, Name) ->
io:format("Errors: ~p~nWarnings: ~p~nAborting...~n", [Err, Warn]),
throw({error, compile_error});
error ->
io:format("Errors encoutered, Aborting...~n", []),
io:format("Errors encountered, Aborting...~n", []),
throw({error, compile_error})
end.
@ -211,6 +218,18 @@ load_migration(Module, Short, Binary) ->
%%------------------------------------------------------------------------------
get_ts_before_last() ->
case lists:member(?TABLE, mnesia:system_info(tables)) of
true -> get_ts_bef_last1(mnesia:dirty_all_keys(?TABLE));
false -> ?FIRST_TS
end.
get_ts_bef_last1([]) -> ?FIRST_TS;
get_ts_bef_last1([TS]) -> TS - 1;
get_ts_bef_last1(List) -> hd(tl(lists:reverse(List))).
%%------------------------------------------------------------------------------
execute_up({Module, Ts}) ->
io:format("Executing up in ~s...~n", [Module]),
Module:up(),