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:
@ -27,7 +27,7 @@
|
|||||||
{application, migresia,
|
{application, migresia,
|
||||||
[
|
[
|
||||||
{description, "Simple mnesia database migration tool"},
|
{description, "Simple mnesia database migration tool"},
|
||||||
{vsn, "0.0.3"},
|
{vsn, "0.0.4"},
|
||||||
{modules, [migresia, migresia_migrations]},
|
{modules, [migresia, migresia_migrations]},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications, [kernel, stdlib]}
|
{applications, [kernel, stdlib]}
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
{"0.0.3",
|
{"0.0.4",
|
||||||
[{"0.0.2",
|
[
|
||||||
|
{"0.0.3",
|
||||||
[{load_module, migresia_migrations},
|
[{load_module, migresia_migrations},
|
||||||
{load_module, migresia}
|
{load_module, migresia}]},
|
||||||
]}],
|
{"0.0.2",
|
||||||
[{"0.0.2",
|
|
||||||
[{load_module, migresia_migrations},
|
[{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}]}
|
||||||
|
]
|
||||||
}.
|
}.
|
||||||
|
@ -25,13 +25,16 @@
|
|||||||
-module(migresia).
|
-module(migresia).
|
||||||
|
|
||||||
-export([start_all_mnesia/0,
|
-export([start_all_mnesia/0,
|
||||||
|
list_nodes/0,
|
||||||
|
list_migrations/0,
|
||||||
ensure_started/1,
|
ensure_started/1,
|
||||||
check/1,
|
check/1,
|
||||||
migrate/0,
|
migrate/0,
|
||||||
migrate/1,
|
migrate/1,
|
||||||
rollback/1,
|
rollback/1,
|
||||||
rollback/2,
|
rollback/2,
|
||||||
list_nodes/0]).
|
rollback_last/0,
|
||||||
|
rollback_last/1]).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -49,6 +52,9 @@ start_all_mnesia() ->
|
|||||||
list_nodes() ->
|
list_nodes() ->
|
||||||
mnesia:table_info(schema, disc_copies).
|
mnesia:table_info(schema, disc_copies).
|
||||||
|
|
||||||
|
list_migrations() ->
|
||||||
|
migresia_migrations:list_migrations().
|
||||||
|
|
||||||
ensure_started_on_remotes(Nodes) ->
|
ensure_started_on_remotes(Nodes) ->
|
||||||
io:format("Ensuring Mnesia is running on nodes:~n~p~n", [Nodes]),
|
io:format("Ensuring Mnesia is running on nodes:~n~p~n", [Nodes]),
|
||||||
{ResL, BadNodes} = rpc:multicall(Nodes, migresia, ensure_started, [mnesia]),
|
{ResL, BadNodes} = rpc:multicall(Nodes, migresia, ensure_started, [mnesia]),
|
||||||
@ -131,14 +137,21 @@ rollback(Srcs, Time) ->
|
|||||||
rollback1(Srcs, Time) ->
|
rollback1(Srcs, Time) ->
|
||||||
io:format("Waiting for tables (max timeout 2 minutes)...~n", []),
|
io:format("Waiting for tables (max timeout 2 minutes)...~n", []),
|
||||||
ok = mnesia:wait_for_tables(mnesia:system_info(tables), 120000),
|
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;
|
{error, _} = Err -> Err;
|
||||||
Ups -> apply_downs(Srcs, Ups, Time)
|
Ups -> apply_downs(Srcs, Ups, Time)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
apply_downs(Srcs, Ups, Time) ->
|
apply_downs(Srcs, Loaded, Time) ->
|
||||||
%% Load the transform function on all nodes, see:
|
%% Load the transform function on all nodes, see:
|
||||||
%% http://toddhalfpenny.com/2012/05/21/possible-erlang-bad-transform-function-solution/
|
%% http://toddhalfpenny.com/2012/05/21/possible-erlang-bad-transform-function-solution/
|
||||||
rpc:multicall(nodes(), migresia_migrations, list_all_ups, [Srcs]),
|
rpc:multicall(nodes(), migresia_migrations, list_applied_ups, [Srcs, Time]),
|
||||||
ToRollBack = lists:reverse([X || {_, Ts} = X <- Ups, Ts > Time]),
|
lists:foreach(fun migresia_migrations:execute_down/1, Loaded).
|
||||||
lists:foreach(fun migresia_migrations:execute_down/1, ToRollBack).
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-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()).
|
||||||
|
@ -25,15 +25,19 @@
|
|||||||
-module(migresia_migrations).
|
-module(migresia_migrations).
|
||||||
|
|
||||||
-export([init_migrations/0,
|
-export([init_migrations/0,
|
||||||
|
list_migrations/0,
|
||||||
list_unapplied_ups/1,
|
list_unapplied_ups/1,
|
||||||
list_all_ups/1,
|
list_applied_ups/2,
|
||||||
get_default_dir/0,
|
get_default_dir/0,
|
||||||
get_priv_dir/1,
|
get_priv_dir/1,
|
||||||
|
get_ts_before_last/0,
|
||||||
execute_up/1,
|
execute_up/1,
|
||||||
execute_down/1]).
|
execute_down/1]).
|
||||||
|
|
||||||
-define(DIR, <<"migrations">>).
|
-define(DIR, <<"migrations">>).
|
||||||
-define(TABLE, schema_migrations).
|
-define(TABLE, schema_migrations).
|
||||||
|
%% Surely no migrations before the first commit in migresia
|
||||||
|
-define(FIRST_TS, 20130404041545).
|
||||||
|
|
||||||
-type mod_bin_list() :: [{module(), binary()}].
|
-type mod_bin_list() :: [{module(), binary()}].
|
||||||
|
|
||||||
@ -53,24 +57,27 @@ init_migrations() ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
list_migrations() ->
|
||||||
|
mnesia:dirty_all_keys(?TABLE).
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
-spec list_unapplied_ups(migresia:migration_sources())
|
-spec list_unapplied_ups(migresia:migration_sources()) -> mod_bin_list().
|
||||||
-> mod_bin_list().
|
|
||||||
list_unapplied_ups({rel_relative_dir, default}) ->
|
list_unapplied_ups({rel_relative_dir, default}) ->
|
||||||
list_unapplied_ups({rel_relative_dir, get_default_dir()});
|
list_unapplied_ups({rel_relative_dir, get_default_dir()});
|
||||||
list_unapplied_ups({rel_relative_dir, DirName}) ->
|
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) ->
|
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().
|
-spec list_applied_ups(migresia:migration_sources(), integer()) ->
|
||||||
list_all_ups({rel_relative_dir, default}) ->
|
mod_bin_list().
|
||||||
list_all_ups({rel_relative_dir, get_default_dir()});
|
list_applied_ups({rel_relative_dir, default}, Time) ->
|
||||||
list_all_ups({rel_relative_dir, DirName}) ->
|
list_applied_ups({rel_relative_dir, get_default_dir()}, Time);
|
||||||
get_all_migrations(get_release_dir(DirName));
|
list_applied_ups({rel_relative_dir, DirName}, Time) ->
|
||||||
list_all_ups(App) when is_atom(App) ->
|
get_applied(get_release_dir(DirName), Time);
|
||||||
get_all_migrations(get_priv_dir(App)).
|
list_applied_ups(App, Time) when is_atom(App) ->
|
||||||
|
get_applied(get_priv_dir(App), Time).
|
||||||
|
|
||||||
get_default_dir() ->
|
get_default_dir() ->
|
||||||
case application:get_env(migresia, rel_relative_dir) of
|
case application:get_env(migresia, rel_relative_dir) of
|
||||||
@ -111,23 +118,37 @@ check_priv_dir(App) ->
|
|||||||
false -> throw({error, enoent})
|
false -> throw({error, enoent})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_migrations(binary()) -> mod_bin_list().
|
-spec get_unapplied(binary()) -> mod_bin_list().
|
||||||
get_migrations(Dir) ->
|
get_unapplied(Dir) ->
|
||||||
ToApply = check_dir(file:list_dir(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
|
case check_table() of
|
||||||
{error, _} = Err -> throw(Err);
|
{error, _} = Err -> throw(Err);
|
||||||
Applied -> compile_and_load(Dir, ToApply, Applied)
|
Applied -> compile_and_load(Dir, FilterFun, Migrations, Applied)
|
||||||
end.
|
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({error, _} = Err) -> throw(Err);
|
||||||
check_dir({ok, Filenames}) -> normalize_names(Filenames, []).
|
check_dir({ok, Filenames}) -> normalize_names(Filenames, []).
|
||||||
|
|
||||||
normalize_names([<<Short:14/bytes, ".erl">>|T], Acc) ->
|
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)
|
normalize_names([<<Short:14/bytes, $_, R/binary>> = Name|T], Acc)
|
||||||
when size(R) >= 4
|
when size(R) >= 4
|
||||||
andalso erlang:binary_part(R, size(R) - 4, 4) == <<".erl">> ->
|
andalso erlang:binary_part(R, size(R) - 4, 4) == <<".erl">> ->
|
||||||
@ -159,30 +180,16 @@ check_table() ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
compile_and_load(Dir, ToApply, Applied) ->
|
compile_and_load(Dir, FilterFun, ToApply, Applied) ->
|
||||||
ToExecute = compile_unapplied(Dir, ToApply, Applied, []),
|
ToLoad = FilterFun(ToApply, Applied),
|
||||||
Fun = fun({Module, Short, Binary}) ->
|
lists:map(fun(X) -> compile_and_load1(Dir, X) end, ToLoad).
|
||||||
load_migration(Module, Short, Binary)
|
|
||||||
end,
|
compile_and_load1(Dir, {Short, Name}) ->
|
||||||
lists:map(Fun, ToExecute).
|
{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) ->
|
compile_file(Dir, Short, Name) ->
|
||||||
File = filename:join(Dir, Name),
|
File = filename:join(Dir, Name),
|
||||||
io:format("Compiling: ~s~n", [File]),
|
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]),
|
io:format("Errors: ~p~nWarnings: ~p~nAborting...~n", [Err, Warn]),
|
||||||
throw({error, compile_error});
|
throw({error, compile_error});
|
||||||
error ->
|
error ->
|
||||||
io:format("Errors encoutered, Aborting...~n", []),
|
io:format("Errors encountered, Aborting...~n", []),
|
||||||
throw({error, compile_error})
|
throw({error, compile_error})
|
||||||
end.
|
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}) ->
|
execute_up({Module, Ts}) ->
|
||||||
io:format("Executing up in ~s...~n", [Module]),
|
io:format("Executing up in ~s...~n", [Module]),
|
||||||
Module:up(),
|
Module:up(),
|
||||||
|
Reference in New Issue
Block a user