%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2018. 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 ~tw 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 ~tw ~n" "** Reason = ~tw, Args = ~tp~n", [Tab, Reason, Def]), [Tab | make_tabs(Tail)]; _ -> io:format("New table ~tw~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:~tp at line ~w unknown\n", [Term,Line]), error(undefined_object) end; collect_data(_Tabs, []) -> []; collect_data(_Tabs, [H|_T]) -> io:format("Object:~tp 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:~tp ~ts\n", [NewLine, File, Str]), error end; {eof,_EndLine} -> eof; Other -> io:format("Error1 **~p~n",[Other]), error end.