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