Database 选择Mnesia中的第二行?

Database 选择Mnesia中的第二行?,database,erlang,mnesia,Database,Erlang,Mnesia,我在Mnesia中有一个很大的表,由于各种原因(这里不重要,比如说我正在远程执行select,结果必须使用一些第三方库通过网络发送),我无法在一个select中选择所有行。我已经将select拆分为一次只检索特定数量的列 e、 g.这是仅检索特定列的select示例: mnesia:dirty_select([table,[{{table,'$1','_','$3','$4','_','_','_'},[],['$$']}]]). 我使用不同的列集运行了两次select,然后合并结果。但现在发

我在Mnesia中有一个很大的表,由于各种原因(这里不重要,比如说我正在远程执行select,结果必须使用一些第三方库通过网络发送),我无法在一个select中选择所有行。我已经将select拆分为一次只检索特定数量的列

e、 g.这是仅检索特定列的select示例:

mnesia:dirty_select([table,[{{table,'$1','_','$3','$4','_','_','_'},[],['$$']}]]).
我使用不同的列集运行了两次select,然后合并结果。但现在发现其中一列也太大,无法在一次选择中检索。所以我想将一个大的select拆分为两个select,每个select只检索该列中的一半行。有没有一种简单的方法可以检索,比如说,每第二行?比如只选择奇数行,然后只选择偶数行?或者是一种检索前半行然后再检索后半行的方法

我尝试从其中一列中选择所有行,然后将其用作检索特定行的索引。这是可行的,但是构建select并执行它需要相当长的时间

编辑:很抱歉,我没有充分强调select正在远程执行这一事实。我知道如何迭代记录或直接访问文件,但这里的挑战是,必须使用相对较少的命令检索这些记录,并且这些命令必须能够远程执行

例如:

  • 仅选择第一列(简单的单
    mnesia:dirty\u选择
    命令)
  • 一旦检索到结果(通过网络),将其分为两组,并用作键来构造select以获取特定记录(每个select将包含一长串要检索的键,但这很好,因为它们是可以通过网络发送的简单Erlang术语)
  • 使用在2中创建的两组键,分两步检索所有行

  • 这是可行的,但并不容易,也不是最优的,因为它双向发送了大量数据。除非选择的构造考虑了每列和每行中包含的特定数据(例如,将所有行与第一列中以字母“A”到“M”开头的名称匹配),否则可能没有简单的解决方案。我只是不确定使用标准Mnesia命令可以实现什么,并希望有一个更简单的解决方案。

    如果您在表上放置一些自动递增整数的键,那么您可以使用QLC大致实现这一点

    Evens = qlc:q([Rec || Rec <- Table, (Rec#table.int_key rem 2) =:= 0]).
    Odds  = qlc:q([Rec || Rec <- Table, (Rec#table.int_key rem 2) =:= 1]).
    

    我创建了一个基于gen_服务器的行为,我将其命名为gen_select。使用它,您可以使用模块属性
    -behavior(gen\u select)
    编写回调模块。在init/1回调中,打开ets或dets文件并定义匹配规范和限制。该过程将在表中分块调用每个记录的
    handle\u record/2
    回调,直到文件结束。我发现这是我一直在做的一些“大数据”工作的一个方便范例。 如果合适的话,您可以在mnesia表的底层ets表上使用它,或者修改它以使用mnesia:select/4

    %%% gen_select.erl
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%% @doc This module implements a behaviour pattern where a potentially
    %%%     large number of records are read from an {@link //stdlib/ets. ets}
    %%%     or {@link //stdlib/dets. dets} table.  This is used in an application
    %%%     to have supervised workers mapping over all the records in a table.
    %%%     The user will call `gen_select:start_link/3', from a supervisor, to
    %%%     create a process which will iterate over the selected records of a
    %%%     table.  The `init/1' callback should open the table to
    %%%     be read and construct a match specification to be used to select 
    %%%     records from the table.  It should return a tuple of the form:
    %%%     ```
    %%%     {ok, TableType, Table, MatchSpec, Limit, State} | {stop, Reason} | ignore
    %%%         TableType :: ets | dets
    %%%         Table :: ets:tid() | atom()  % when Type=ets
    %%%         Table :: dets:tab_name()     % when Type=dets
    %%%         MatchSpec :: match_spec()    % see ets:select/2
    %%%         Limit :: integer()           % see ets:select/3
    %%%         State :: term()
    %%%         Reason :: term()
    %%%     '''
    %%% After initialization {@link //stdlib/ets:select/3. ets:select/3}
    %%% or {@link //stdlib/dets:select/3. dets:select/3} will be called
    %%% using the `match_spec()' and `Limit' returned by `init/`'.  The
    %%% callback function `handle_record/2' will then be called for each
    %%% record returned then `select/1' will be called to get more records.
    %%% This is repeated until the end of the table is reached when the
    %%% callback `terminate/2' is called with `Reason=eof'..
    %%% 
    -module(gen_select).
    -author('vance@wavenet.lk').
    
    %% export the gen_select API
    -export([start_link/3]).
    
    %% export the callbacks needed for a system process
    -export([system_continue/3, system_terminate/4, system_code_change/4]).
    -export([format_status/2]).
    
    %% exports used internally
    -export([init_it/6]).
    
    %% define the callback exports of a module behaving as gen_select
    -type state() :: term().
    -callback init(Args :: term()) ->
        {ok, TableType :: ets | dets, Table :: ets:tid() | atom() | dets:tab_name(),
            MatchSpec :: ets:match_spec(), Limit :: non_neg_integer(), State :: state()}
            | {stop, Reason :: term()} | ignore.
    -callback handle_record(Record :: tuple(), State :: state()) ->
        {next_record, NewState :: state()}
            | {stop, Reason :: term(), NewState :: state()}.
    -callback terminate(Reason :: eof | term(), State :: state()) ->
        any().
    
    -import(error_logger, [format/2]).
    
    %%----------------------------------------------------------------------
    %%  gen_select API
    %%----------------------------------------------------------------------
    
    -spec start_link(Mod :: atom(), Args :: term(),
            Options :: gen:options()) -> gen:start_ret().
    %% @doc Creates a {@module} process as part of a supervision tree.
    %% 
    start_link(Mod, Args, Options) ->
        gen:start(?MODULE, link, Mod, Args, Options).
    
    %%----------------------------------------------------------------------
    %%  internal exports
    %%----------------------------------------------------------------------
    
    -spec init_it(Starter :: pid(), LinkP :: gen:linkage(), Pid :: pid(),
            CallBackMod :: atom(), Args :: term(), Options :: gen:options()) ->
        no_return().
    %% @doc Called by {@link //stdlib/gen:start/5. gen:start/5} to initialize
    %%  the process.
    %%  Copied from //stdlib/gen_server:init_it/6.
    %% @hidden
    init_it(Starter, Parent, Pid, CallBackMod, Args, Options) ->
        Debug = debug_options(Pid, Options),
        case catch CallBackMod:init(Args) of
            {ok, TableMod, Table, MatchSpec, Limit, State} ->
                proc_lib:init_ack(Starter, {ok, self()}),
                case catch ets:select(Table, MatchSpec, Limit) of
                    {Matches, Cont} when is_list(Matches) ->
                        loop1(Parent, CallBackMod, Debug, State,
                                TableMod, Cont, Matches);
                    '$end_of_table' ->
                        proc_lib:init_ack(Starter, {error, eof}),
                        exit(eof);
                    {error, Reason} ->
                        proc_lib:init_ack(Starter, {error, Reason}),
                        exit(Reason);
                    {'EXIT', Reason} ->
                        proc_lib:init_ack(Starter, {error, Reason}),
                        exit(Reason)
                end;
            {stop, Reason} ->
                proc_lib:init_ack(Starter, {error, Reason}),
                exit(Reason);
            ignore ->
                proc_lib:init_ack(Starter, ignore),
                exit(normal);
            {'EXIT', Reason} ->
                proc_lib:init_ack(Starter, {error, Reason}),
                exit(Reason);
            Else ->
                Error = {bad_return_value, Else},
                proc_lib:init_ack(Starter, {error, Error}),
                exit(Error)
        end.
    
    %%----------------------------------------------------------------------
    %%  system process callbacks
    %%----------------------------------------------------------------------
    
    -type misc() :: [CallBackMod :: atom() | [State :: state()
            | [TableMod :: atom() | [Cont :: term()
            | [Matches :: [tuple()] | []]]]]].
    
    -spec system_continue(Parent :: pid(), Debug :: [gen:dbg_opt()],
            Misc :: misc()) -> no_return().
    %% @doc Called by {@link //sys:handle_system_msg/6} to continue.
    %% @private
    system_continue(Parent, Debug, [CallBackMod, State,
            TableMod, Cont, Matches]) ->
        loop1(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches).
    
    -spec system_terminate(Reason :: term(), Parent :: pid(),
            Debug :: [gen:dbg_opt()], Misc :: misc()) -> no_return().
    %% @doc Called by {@link //sys:handle_system_msg/6} to terminate.
    %% @private
    system_terminate(Reason, _Parent, Debug, [CallBackMod, State,
            _TableMod, _Cont, _Matches]) ->
        terminate(Reason, CallBackMod, Debug, State).
    
    -spec system_code_change(Misc :: misc(), Module :: atom(),
            OldVsn :: undefined | term(), Extra :: term()) ->
        {ok, NewMisc :: misc()}.
    %% @doc Called by {@link //sys:handle_system_msg/6} to update `Misc'.
    %% @private
    system_code_change([CallBackMod, State, TableMod, Cont, Matches],
            _Module, OldVsn, Extra) ->
        case catch CallBackMod:code_change(OldVsn, State, Extra) of
            {ok, NewState} ->
                {ok, [CallBackMod, NewState, TableMod, Cont, Matches]};
            Other ->
                Other
        end.
    
    -type pdict() :: [{Key :: term(), Value :: term()}].
    -type status_data() :: [PDict :: pdict() | [SysState :: term()
            | [Parent :: pid() | [Debug :: [gen:dbg_opt()] | [Misc :: misc() | []]]]]].
    -spec format_status(Opt :: normal | terminate, StatusData :: status_data()) ->
        [tuple()].
    %% @doc Called by {@link //sys:get_status/1} to print state.
    %% @private
    format_status(Opt, [PDict, SysState, Parent, Debug, 
            [CallBackMod, State, _TableMod, _Cont, _Matches]]) ->
        Header = gen:format_status_header("Status for table reader", self()),
        Log = sys:get_debug(log, Debug, []),
        DefaultStatus = [{data, [{"State", State}]}],
        Specfic = case erlang:function_exported(CallBackMod, format_status, 2) of
            true ->
                case catch CallBackMod:format_status(Opt, [PDict, State]) of
                    {'EXIT', _} ->
                        DefaultStatus;
                    StatusList when is_list(StatusList) ->
                        StatusList;
                    Else ->
                        [Else]
                end;
            _ ->
                DefaultStatus
        end,
        [{header, Header},
                {data, [{"Status", SysState},
                        {"Parent", Parent},
                        {"Logged events", Log}]}
                | Specfic].
    
    %%----------------------------------------------------------------------
    %%  internal functions
    %%----------------------------------------------------------------------
    
    -spec loop1(Parent :: pid(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state(), TableMod :: atom(),
            Cont :: term(), Matches :: [tuple()]) -> no_return().
    %% @doc Main loop.
    %%  Copied from //stdlib/gen_server:loop1/6.
    %% @hidden
    loop1(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches) ->
        receive
            {system, From, Req} ->
                sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
                        [CallBackMod, State, TableMod, Cont, Matches]);
            {'EXIT', Parent, Reason} ->
                terminate(Reason, CallBackMod, Debug, State);
            Msg ->
                sys:handle_debug(Debug, fun print_event/3, self(), {in, Msg})
        after 0 ->
            loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches)
        end.
    
    -spec loop2(Parent :: pid(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state(), TableMod :: atom(), Cont :: term(),
            Matches :: [tuple()]) -> no_return().
    %% @doc Run the `select/1' function.
    %% @hidden
    loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, [H | T]) ->
        case catch CallBackMod:handle_record(H, State) of
            {next_record, NewState} ->
                loop1(Parent, CallBackMod, Debug, NewState, TableMod, Cont, T);
            {stop, Reason, NewState} ->
                terminate(Reason, CallBackMod, Debug, NewState);
            {'EXIT', Reason} ->
                terminate(Reason, CallBackMod, Debug, State)
        end;
    loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, []) ->
        case catch TableMod:select(Cont) of
            {Matches, NewCont} when is_list(Matches) ->
                sys:handle_debug(Debug, fun print_event/3, self(), {read, Matches}),
                loop1(Parent, CallBackMod, Debug, State, TableMod, NewCont, Matches);
            '$end_of_table' ->
                terminate(eof, CallBackMod, Debug, State);
            {error, Reason} ->
                terminate(Reason, CallBackMod, Debug, State);
            {'EXIT', Reason} ->
                terminate(Reason, CallBackMod, Debug, State)
        end.
    
    -spec terminate(Reason :: term(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state()) -> no_return().
    %% @doc Terminate the {@module} process.
    %%  Copied from //stdlib/gen_server:terminate/6.
    %% @hidden
    terminate(Reason, CallBackMod, Debug, State) ->
        case catch CallBackMod:terminate(Reason, State) of
            {'EXIT', R} ->
                error_info(R, State, Debug),
                exit(R);
            _ ->
                case Reason of
                    normal ->
                        exit(normal);
                    shutdown ->
                        exit(shutdown);
                    {shutdown, _} = Shutdown ->
                        exit(Shutdown);
                    _ ->
                        FmtState = case erlang:function_exported(CallBackMod,
                                format_status, 2) of
                            true ->
                                case catch CallBackMod:format_status(terminate,
                                        [get(), State]) of
                                    {'EXIT', _} ->
                                        State;
                                    Else ->
                                        Else
                                end;
                            _ ->
                                State
                        end,
                        error_info(Reason, FmtState, Debug),
                        exit(Reason)
                end
        end.
    
    -spec error_info(Reason :: term(), State :: state(),
            Debug :: [gen:dbg_opt()]) -> ok.
    %% @doc Print error log message.
    %%  Copied from //stdlib/gen_server:error_info/5.
    %% @hidden
    error_info(Reason, State, Debug) ->
        Reason1 = case Reason of
            {undef, [{M, F, A, L} | MFAs]} ->
                case code:is_loaded(M) of
                    false ->
                        {'module could not be loaded', [{M, F, A, L} | MFAs]};
                    _ ->
                        case erlang:function_exported(M, F, length(A)) of
                            true ->
                                Reason;
                            false ->
                                {'function not exported', [{M, F, A, L} | MFAs]}
                        end
                end;
            _ ->
                Reason
       end,
        format("** Table reader ~p terminating \n"
                "** When Server state == ~p~n"
                "** Reason for termination == ~n** ~p~n",
                [self(), State, Reason1]),
        sys:print_log(Debug),
        ok.
    
    %% Copied from //stdlib/gen_server:opt/2
    opt(Op, [{Op, Value} | _]) ->
        {ok, Value};
    opt(Op, [_ | Options]) ->
        opt(Op, Options);
    opt(_, []) ->
        false.
    
    %% Copied from //stdlib/gen_server:debug_options/2
    debug_options(Name, Opts) ->
        case opt(debug, Opts) of
            {ok, Options} ->
                dbg_options(Name, Options);
            _ ->
                dbg_options(Name, [])
        end.
    
    %% Copied from //stdlib/gen_server:dbg_options/2
    dbg_options(Name, []) ->
        Opts = case init:get_argument(generic_debug) of
            error ->
                [];
            _ ->
                [log, statistics]
        end,
        dbg_opts(Name, Opts);
    dbg_options(Name, Opts) ->
        dbg_opts(Name, Opts).
    
    %% Copied from //stdlib/gen_server:dbg_opts/2
    dbg_opts(Name, Opts) ->
        case catch sys:debug_options(Opts) of
            {'EXIT',_} ->
                format("~p: ignoring erroneous debug options - ~p~n",
                        [Name, Opts]),
                [];
            Dbg ->
                Dbg
        end.
    
    -spec print_event(IoDevice :: io:device(), Event :: term(), Pid :: pid()) -> ok.
    %% @doc Called by {@link //sys:handle_debug/4} to print trace events.
    print_event(Dev, {in, Msg}, Pid) ->
        io:format(Dev, "*DBG* ~p got ~p~n", [Pid, Msg]);
    print_event(Dev, {read, Matches}, Pid) ->
        io:format(Dev, "*DBG* ~p read ~b records~n", [Pid, length(Matches)]).
    

    我知道这与您的问题没有直接关系,但是在一次选择中检索太多数据的症状是什么?拆分查询时需要记住的一件事是竞争条件的可能性增加。阅读您的问题,您面临的限制似乎不是来自Mnesia或Erlang。那么,为什么不在一次选择中获得对象列表,然后根据第三方库的要求将结果拆分为多个部分呢?这太解释了为什么我需要使用多个选择来检索数据。我知道这涉及到的所有问题。正如我所说,我正在远程执行它们,问题是第三方库在执行一个命令后只能发送有限的数据。但无论如何,这不是问题所在,不过我很感谢您的输入。对整个表进行
    迭代
    ,以便将符合您的规范的对象以块或位的形式传输到远程节点怎么样?@muzaayahoshua,正如我提到的,我正在远程执行select。我无法通过网络匹配每条记录,因为那样会降低性能。我唯一能做的就是选择。谢谢@d11wtq,但第一种方法并不好。我不拥有该表,因此无法添加新列。另外,正如我提到的,我正在远程执行select。第二种方法有效吗?例如,乐趣是否会通过网络发送并在远程节点上执行?谢谢@VanceShipley,但正如我提到的,我正在远程执行select。我无法访问数据库文件,因为它位于远程节点上。我的意思是,我可以远程访问文件,就像我可以执行select一样,但是通过网络将其分块会降低性能。
    %%% gen_select.erl
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%% @doc This module implements a behaviour pattern where a potentially
    %%%     large number of records are read from an {@link //stdlib/ets. ets}
    %%%     or {@link //stdlib/dets. dets} table.  This is used in an application
    %%%     to have supervised workers mapping over all the records in a table.
    %%%     The user will call `gen_select:start_link/3', from a supervisor, to
    %%%     create a process which will iterate over the selected records of a
    %%%     table.  The `init/1' callback should open the table to
    %%%     be read and construct a match specification to be used to select 
    %%%     records from the table.  It should return a tuple of the form:
    %%%     ```
    %%%     {ok, TableType, Table, MatchSpec, Limit, State} | {stop, Reason} | ignore
    %%%         TableType :: ets | dets
    %%%         Table :: ets:tid() | atom()  % when Type=ets
    %%%         Table :: dets:tab_name()     % when Type=dets
    %%%         MatchSpec :: match_spec()    % see ets:select/2
    %%%         Limit :: integer()           % see ets:select/3
    %%%         State :: term()
    %%%         Reason :: term()
    %%%     '''
    %%% After initialization {@link //stdlib/ets:select/3. ets:select/3}
    %%% or {@link //stdlib/dets:select/3. dets:select/3} will be called
    %%% using the `match_spec()' and `Limit' returned by `init/`'.  The
    %%% callback function `handle_record/2' will then be called for each
    %%% record returned then `select/1' will be called to get more records.
    %%% This is repeated until the end of the table is reached when the
    %%% callback `terminate/2' is called with `Reason=eof'..
    %%% 
    -module(gen_select).
    -author('vance@wavenet.lk').
    
    %% export the gen_select API
    -export([start_link/3]).
    
    %% export the callbacks needed for a system process
    -export([system_continue/3, system_terminate/4, system_code_change/4]).
    -export([format_status/2]).
    
    %% exports used internally
    -export([init_it/6]).
    
    %% define the callback exports of a module behaving as gen_select
    -type state() :: term().
    -callback init(Args :: term()) ->
        {ok, TableType :: ets | dets, Table :: ets:tid() | atom() | dets:tab_name(),
            MatchSpec :: ets:match_spec(), Limit :: non_neg_integer(), State :: state()}
            | {stop, Reason :: term()} | ignore.
    -callback handle_record(Record :: tuple(), State :: state()) ->
        {next_record, NewState :: state()}
            | {stop, Reason :: term(), NewState :: state()}.
    -callback terminate(Reason :: eof | term(), State :: state()) ->
        any().
    
    -import(error_logger, [format/2]).
    
    %%----------------------------------------------------------------------
    %%  gen_select API
    %%----------------------------------------------------------------------
    
    -spec start_link(Mod :: atom(), Args :: term(),
            Options :: gen:options()) -> gen:start_ret().
    %% @doc Creates a {@module} process as part of a supervision tree.
    %% 
    start_link(Mod, Args, Options) ->
        gen:start(?MODULE, link, Mod, Args, Options).
    
    %%----------------------------------------------------------------------
    %%  internal exports
    %%----------------------------------------------------------------------
    
    -spec init_it(Starter :: pid(), LinkP :: gen:linkage(), Pid :: pid(),
            CallBackMod :: atom(), Args :: term(), Options :: gen:options()) ->
        no_return().
    %% @doc Called by {@link //stdlib/gen:start/5. gen:start/5} to initialize
    %%  the process.
    %%  Copied from //stdlib/gen_server:init_it/6.
    %% @hidden
    init_it(Starter, Parent, Pid, CallBackMod, Args, Options) ->
        Debug = debug_options(Pid, Options),
        case catch CallBackMod:init(Args) of
            {ok, TableMod, Table, MatchSpec, Limit, State} ->
                proc_lib:init_ack(Starter, {ok, self()}),
                case catch ets:select(Table, MatchSpec, Limit) of
                    {Matches, Cont} when is_list(Matches) ->
                        loop1(Parent, CallBackMod, Debug, State,
                                TableMod, Cont, Matches);
                    '$end_of_table' ->
                        proc_lib:init_ack(Starter, {error, eof}),
                        exit(eof);
                    {error, Reason} ->
                        proc_lib:init_ack(Starter, {error, Reason}),
                        exit(Reason);
                    {'EXIT', Reason} ->
                        proc_lib:init_ack(Starter, {error, Reason}),
                        exit(Reason)
                end;
            {stop, Reason} ->
                proc_lib:init_ack(Starter, {error, Reason}),
                exit(Reason);
            ignore ->
                proc_lib:init_ack(Starter, ignore),
                exit(normal);
            {'EXIT', Reason} ->
                proc_lib:init_ack(Starter, {error, Reason}),
                exit(Reason);
            Else ->
                Error = {bad_return_value, Else},
                proc_lib:init_ack(Starter, {error, Error}),
                exit(Error)
        end.
    
    %%----------------------------------------------------------------------
    %%  system process callbacks
    %%----------------------------------------------------------------------
    
    -type misc() :: [CallBackMod :: atom() | [State :: state()
            | [TableMod :: atom() | [Cont :: term()
            | [Matches :: [tuple()] | []]]]]].
    
    -spec system_continue(Parent :: pid(), Debug :: [gen:dbg_opt()],
            Misc :: misc()) -> no_return().
    %% @doc Called by {@link //sys:handle_system_msg/6} to continue.
    %% @private
    system_continue(Parent, Debug, [CallBackMod, State,
            TableMod, Cont, Matches]) ->
        loop1(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches).
    
    -spec system_terminate(Reason :: term(), Parent :: pid(),
            Debug :: [gen:dbg_opt()], Misc :: misc()) -> no_return().
    %% @doc Called by {@link //sys:handle_system_msg/6} to terminate.
    %% @private
    system_terminate(Reason, _Parent, Debug, [CallBackMod, State,
            _TableMod, _Cont, _Matches]) ->
        terminate(Reason, CallBackMod, Debug, State).
    
    -spec system_code_change(Misc :: misc(), Module :: atom(),
            OldVsn :: undefined | term(), Extra :: term()) ->
        {ok, NewMisc :: misc()}.
    %% @doc Called by {@link //sys:handle_system_msg/6} to update `Misc'.
    %% @private
    system_code_change([CallBackMod, State, TableMod, Cont, Matches],
            _Module, OldVsn, Extra) ->
        case catch CallBackMod:code_change(OldVsn, State, Extra) of
            {ok, NewState} ->
                {ok, [CallBackMod, NewState, TableMod, Cont, Matches]};
            Other ->
                Other
        end.
    
    -type pdict() :: [{Key :: term(), Value :: term()}].
    -type status_data() :: [PDict :: pdict() | [SysState :: term()
            | [Parent :: pid() | [Debug :: [gen:dbg_opt()] | [Misc :: misc() | []]]]]].
    -spec format_status(Opt :: normal | terminate, StatusData :: status_data()) ->
        [tuple()].
    %% @doc Called by {@link //sys:get_status/1} to print state.
    %% @private
    format_status(Opt, [PDict, SysState, Parent, Debug, 
            [CallBackMod, State, _TableMod, _Cont, _Matches]]) ->
        Header = gen:format_status_header("Status for table reader", self()),
        Log = sys:get_debug(log, Debug, []),
        DefaultStatus = [{data, [{"State", State}]}],
        Specfic = case erlang:function_exported(CallBackMod, format_status, 2) of
            true ->
                case catch CallBackMod:format_status(Opt, [PDict, State]) of
                    {'EXIT', _} ->
                        DefaultStatus;
                    StatusList when is_list(StatusList) ->
                        StatusList;
                    Else ->
                        [Else]
                end;
            _ ->
                DefaultStatus
        end,
        [{header, Header},
                {data, [{"Status", SysState},
                        {"Parent", Parent},
                        {"Logged events", Log}]}
                | Specfic].
    
    %%----------------------------------------------------------------------
    %%  internal functions
    %%----------------------------------------------------------------------
    
    -spec loop1(Parent :: pid(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state(), TableMod :: atom(),
            Cont :: term(), Matches :: [tuple()]) -> no_return().
    %% @doc Main loop.
    %%  Copied from //stdlib/gen_server:loop1/6.
    %% @hidden
    loop1(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches) ->
        receive
            {system, From, Req} ->
                sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
                        [CallBackMod, State, TableMod, Cont, Matches]);
            {'EXIT', Parent, Reason} ->
                terminate(Reason, CallBackMod, Debug, State);
            Msg ->
                sys:handle_debug(Debug, fun print_event/3, self(), {in, Msg})
        after 0 ->
            loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, Matches)
        end.
    
    -spec loop2(Parent :: pid(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state(), TableMod :: atom(), Cont :: term(),
            Matches :: [tuple()]) -> no_return().
    %% @doc Run the `select/1' function.
    %% @hidden
    loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, [H | T]) ->
        case catch CallBackMod:handle_record(H, State) of
            {next_record, NewState} ->
                loop1(Parent, CallBackMod, Debug, NewState, TableMod, Cont, T);
            {stop, Reason, NewState} ->
                terminate(Reason, CallBackMod, Debug, NewState);
            {'EXIT', Reason} ->
                terminate(Reason, CallBackMod, Debug, State)
        end;
    loop2(Parent, CallBackMod, Debug, State, TableMod, Cont, []) ->
        case catch TableMod:select(Cont) of
            {Matches, NewCont} when is_list(Matches) ->
                sys:handle_debug(Debug, fun print_event/3, self(), {read, Matches}),
                loop1(Parent, CallBackMod, Debug, State, TableMod, NewCont, Matches);
            '$end_of_table' ->
                terminate(eof, CallBackMod, Debug, State);
            {error, Reason} ->
                terminate(Reason, CallBackMod, Debug, State);
            {'EXIT', Reason} ->
                terminate(Reason, CallBackMod, Debug, State)
        end.
    
    -spec terminate(Reason :: term(), CallBackMod :: atom(), Debug :: [gen:dbg_opt()],
            State :: state()) -> no_return().
    %% @doc Terminate the {@module} process.
    %%  Copied from //stdlib/gen_server:terminate/6.
    %% @hidden
    terminate(Reason, CallBackMod, Debug, State) ->
        case catch CallBackMod:terminate(Reason, State) of
            {'EXIT', R} ->
                error_info(R, State, Debug),
                exit(R);
            _ ->
                case Reason of
                    normal ->
                        exit(normal);
                    shutdown ->
                        exit(shutdown);
                    {shutdown, _} = Shutdown ->
                        exit(Shutdown);
                    _ ->
                        FmtState = case erlang:function_exported(CallBackMod,
                                format_status, 2) of
                            true ->
                                case catch CallBackMod:format_status(terminate,
                                        [get(), State]) of
                                    {'EXIT', _} ->
                                        State;
                                    Else ->
                                        Else
                                end;
                            _ ->
                                State
                        end,
                        error_info(Reason, FmtState, Debug),
                        exit(Reason)
                end
        end.
    
    -spec error_info(Reason :: term(), State :: state(),
            Debug :: [gen:dbg_opt()]) -> ok.
    %% @doc Print error log message.
    %%  Copied from //stdlib/gen_server:error_info/5.
    %% @hidden
    error_info(Reason, State, Debug) ->
        Reason1 = case Reason of
            {undef, [{M, F, A, L} | MFAs]} ->
                case code:is_loaded(M) of
                    false ->
                        {'module could not be loaded', [{M, F, A, L} | MFAs]};
                    _ ->
                        case erlang:function_exported(M, F, length(A)) of
                            true ->
                                Reason;
                            false ->
                                {'function not exported', [{M, F, A, L} | MFAs]}
                        end
                end;
            _ ->
                Reason
       end,
        format("** Table reader ~p terminating \n"
                "** When Server state == ~p~n"
                "** Reason for termination == ~n** ~p~n",
                [self(), State, Reason1]),
        sys:print_log(Debug),
        ok.
    
    %% Copied from //stdlib/gen_server:opt/2
    opt(Op, [{Op, Value} | _]) ->
        {ok, Value};
    opt(Op, [_ | Options]) ->
        opt(Op, Options);
    opt(_, []) ->
        false.
    
    %% Copied from //stdlib/gen_server:debug_options/2
    debug_options(Name, Opts) ->
        case opt(debug, Opts) of
            {ok, Options} ->
                dbg_options(Name, Options);
            _ ->
                dbg_options(Name, [])
        end.
    
    %% Copied from //stdlib/gen_server:dbg_options/2
    dbg_options(Name, []) ->
        Opts = case init:get_argument(generic_debug) of
            error ->
                [];
            _ ->
                [log, statistics]
        end,
        dbg_opts(Name, Opts);
    dbg_options(Name, Opts) ->
        dbg_opts(Name, Opts).
    
    %% Copied from //stdlib/gen_server:dbg_opts/2
    dbg_opts(Name, Opts) ->
        case catch sys:debug_options(Opts) of
            {'EXIT',_} ->
                format("~p: ignoring erroneous debug options - ~p~n",
                        [Name, Opts]),
                [];
            Dbg ->
                Dbg
        end.
    
    -spec print_event(IoDevice :: io:device(), Event :: term(), Pid :: pid()) -> ok.
    %% @doc Called by {@link //sys:handle_debug/4} to print trace events.
    print_event(Dev, {in, Msg}, Pid) ->
        io:format(Dev, "*DBG* ~p got ~p~n", [Pid, Msg]);
    print_event(Dev, {read, Matches}, Pid) ->
        io:format(Dev, "*DBG* ~p read ~b records~n", [Pid, length(Matches)]).