Use yconf validator for custom Mnesia schemas

This commit is contained in:
Evgeny Khramtsov 2019-06-27 15:23:21 +03:00
parent 39cf8d86d6
commit 6011135d24
1 changed files with 43 additions and 64 deletions

View File

@ -45,8 +45,15 @@
-include("logger.hrl").
-include("ejabberd_stacktrace.hrl").
-record(state, {tables = #{} :: map(),
schema = [] :: [{atom(), [{atom(), any()}]}]}).
-record(state, {tables = #{} :: tables(),
schema = [] :: [{atom(), custom_schema()}]}).
-type tables() :: #{atom() => {[{atom(), term()}], term()}}.
-type custom_schema() :: [{ram_copies | disc_copies | disc_only_copies, [node()]} |
{local_content, boolean()} |
{type, set | ordered_set | bag} |
{attributes, [atom()]} |
{index, [atom()]}].
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
@ -201,79 +208,51 @@ schema(Name, Default, Schema) ->
[Name, TabDefs]),
TabDefs;
false ->
?DEBUG("No custom Mnesia schema for table '~s' found",
[Name]),
Default
end.
-spec read_schema_file() -> [{atom(), custom_schema()}].
read_schema_file() ->
File = schema_path(),
case fast_yaml:decode_from_file(File, [plain_as_atom]) of
{ok, [Defs|_]} ->
?INFO_MSG("Using custom Mnesia schema from ~s", [File]),
lists:flatmap(
fun({Tab, Opts}) ->
case validate_schema_opts(File, Opts) of
{ok, NewOpts} ->
[{Tab, lists:ukeysort(1, NewOpts)}];
error ->
[]
end
end, Defs);
{ok, []} ->
?WARNING_MSG("Mnesia schema file ~s is empty", [File]),
[];
{ok, Y} ->
case econf:validate(validator(), lists:flatten(Y)) of
{ok, []} ->
?WARNING_MSG("Mnesia schema file ~s is empty", [File]),
[];
{ok, Config} ->
lists:map(
fun({Tab, Opts}) ->
{Tab, lists:map(
fun({storage_type, T}) -> {T, [node()]};
(Other) -> Other
end, Opts)}
end, Config);
{error, Reason, Ctx} ->
?ERROR_MSG("Failed to read Mnesia schema from ~s: ~s",
[File, econf:format_error(Reason, Ctx)]),
[]
end;
{error, enoent} ->
?DEBUG("No custom Mnesia schema file found", []),
[];
?DEBUG("No custom Mnesia schema file found at ~s", [File]),
[];
{error, Reason} ->
?ERROR_MSG("Failed to read Mnesia schema file ~s: ~s",
[File, fast_yaml:format_error(Reason)]),
[]
[File, fast_yaml:format_error(Reason)])
end.
validate_schema_opts(File, Opts) ->
try {ok, lists:map(
fun({storage_type, Type}) when Type == ram_copies;
Type == disc_copies;
Type == disc_only_copies ->
{Type, [node()]};
({storage_type, _} = Opt) ->
erlang:error({invalid_value, Opt});
({local_content, Bool}) when is_boolean(Bool) ->
{local_content, Bool};
({local_content, _} = Opt) ->
erlang:error({invalid_value, Opt});
({type, Type}) when Type == set;
Type == ordered_set;
Type == bag ->
{type, Type};
({type, _} = Opt) ->
erlang:error({invalid_value, Opt});
({attributes, Attrs} = Opt) ->
try lists:all(fun is_atom/1, Attrs) of
true -> {attributes, Attrs};
false -> erlang:error({invalid_value, Opt})
catch _:_ -> erlang:error({invalid_value, Opt})
end;
({index, Indexes} = Opt) ->
try lists:all(fun is_atom/1, Indexes) of
true -> {index, Indexes};
false -> erlang:error({invalid_value, Opt})
catch _:_ -> erlang:error({invalid_value, Opt})
end;
(Opt) ->
erlang:error({unknown_option, Opt})
end, Opts)}
catch _:{invalid_value, {Opt, Val}} ->
?ERROR_MSG("Mnesia schema ~s is incorrect: invalid value ~p of "
"option '~s'", [File, Val, Opt]),
error;
_:{unknown_option, Opt} ->
?ERROR_MSG("Mnesia schema ~s is incorrect: unknown option ~p",
[File, Opt]),
error
end.
-spec validator() -> econf:validator().
validator() ->
econf:map(
econf:atom(),
econf:options(
#{storage_type => econf:enum([ram_copies, disc_copies, disc_only_copies]),
local_content => econf:bool(),
type => econf:enum([set, ordered_set, bag]),
attributes => econf:list(econf:atom()),
index => econf:list(econf:atom())},
[{return, orddict}, unique]),
[unique]).
create(Name, TabDef) ->
?INFO_MSG("Creating Mnesia table '~s'", [Name]),