%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(mnesia_text).
%% Avoid warning for local function error/1 clashing with autoimported BIF.
-compile({no_auto_import,[error/1]}).
-export([parse/1, file/1, load_textfile/1, dump_to_textfile/1]).
load_textfile(File) ->
ensure_started(),
case parse(File) of
{ok, {Tabs, Data}} ->
Badtabs = make_tabs(lists:map(fun validate_tab/1, Tabs)),
load_data(del_data(Badtabs, Data, []));
Other ->
Other
end.
dump_to_textfile(File) ->
dump_to_textfile(mnesia_lib:is_running(), file:open(File, [write])).
dump_to_textfile(yes, {ok, F}) ->
Tabs = lists:delete(schema, mnesia_lib:local_active_tables()),
Defs = lists:map(fun(T) -> {T, [{record_name, mnesia_lib:val({T, record_name})},
{attributes, mnesia_lib:val({T, attributes})}]}
end,
Tabs),
io:format(F, "~p.~n", [{tables, Defs}]),
lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
file:close(F);
dump_to_textfile(_,_) -> error.
dump_tab(F, T) ->
W = mnesia_lib:val({T, wild_pattern}),
{atomic,All} = mnesia:transaction(fun() -> mnesia:match_object(T, W, read) end),
lists:foreach(fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
ensure_started() ->
case mnesia_lib:is_running() of
yes ->
yes;
no ->
case mnesia_lib:exists(mnesia_lib:dir("schema.DAT")) of
true ->
mnesia:start();
false ->
mnesia:create_schema([node()]),
mnesia:start()
end
end.
del_data(Bad, [H|T], Ack) ->
case lists:member(element(1, H), Bad) of
true -> del_data(Bad, T, Ack);
false -> del_data(Bad, T, [H|Ack])
end;
del_data(_Bad, [], Ack) ->
lists:reverse(Ack).
%% Tis the place to call the validate func in mnesia_schema
validate_tab({Tabname, List}) ->
{Tabname, List};
validate_tab({Tabname, RecName, List}) ->
{Tabname, RecName, List};
validate_tab(_) -> error(badtab).
make_tabs([{Tab, Def} | Tail]) ->
try mnesia:table_info(Tab, where_to_read) of
Node ->
io:format("** Table ~w already exists on ~p, just entering data~n",
[Tab, Node]),
make_tabs(Tail)
catch exit:_ -> %% non-existing table
case mnesia:create_table(Tab, Def) of
{aborted, Reason} ->
io:format("** Failed to create table ~w ~n"
"** Reason = ~w, Args = ~p~n",
[Tab, Reason, Def]),
[Tab | make_tabs(Tail)];
_ ->
io:format("New table ~w~n", [Tab]),
make_tabs(Tail)
end
end;
make_tabs([]) ->
[].
load_data(L) ->
mnesia:transaction(fun() ->
F = fun(X) ->
Tab = element(1, X),
RN = mnesia:table_info(Tab, record_name),
Rec = setelement(1, X, RN),
mnesia:write(Tab, Rec, write) end,
lists:foreach(F, L)
end).
parse(File) ->
case file(File) of
{ok, Terms} ->
try collect(Terms) of
Other -> {ok, Other}
catch throw:Error -> Error
end;
Other ->
Other
end.
collect([{_, {tables, Tabs}}|L]) ->
{Tabs, collect_data(Tabs, L)};
collect(_) ->
io:format("No tables found\n", []),
error(bad_header).
collect_data(Tabs, [{Line, Term} | Tail]) when is_tuple(Term) ->
case lists:keysearch(element(1, Term), 1, Tabs) of
{value, _} ->
[Term | collect_data(Tabs, Tail)];
_Other ->
io:format("Object:~p at line ~w unknown\n", [Term,Line]),
error(undefined_object)
end;
collect_data(_Tabs, []) -> [];
collect_data(_Tabs, [H|_T]) ->
io:format("Object:~p unknown\n", [H]),
error(undefined_object).
error(What) -> throw({error, What}).
file(File) ->
case file:open(File, [read]) of
{ok, Stream} ->
Res = read_terms(Stream, File, 1, []),
file:close(Stream),
Res;
_Other ->
{error, open}
end.
read_terms(Stream, File, Line, L) ->
case read_term_from_stream(Stream, File, Line) of
{ok, Term, NextLine} ->
read_terms(Stream, File, NextLine, [Term|L]);
error ->
{error, read};
eof ->
{ok, lists:reverse(L)}
end.
read_term_from_stream(Stream, File, Line) ->
R = io:request(Stream, {get_until,'',erl_scan,tokens,[Line]}),
case R of
{ok,Toks,EndLine} ->
case erl_parse:parse_term(Toks) of
{ok, Term} ->
{ok, {Line, Term}, EndLine};
{error, {NewLine,Mod,What}} ->
Str = Mod:format_error(What),
io:format("Error in line:~p of:~p ~s\n",
[NewLine, File, Str]),
error
end;
{eof,_EndLine} ->
eof;
Other ->
io:format("Error1 **~p~n",[Other]),
error
end.