qlc.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,743 行 · 第 1/5 页

ERL
1,743
字号
cursor_process(H, GUnique, GCache, TmpDir, SpawnOptions, MaxList) ->    Parent = self(),    Setup = #setup{parent = Parent},    CF = fun() ->                  %% Unless exit/2 is trapped no cleanup can be done.                 %% The user is assumed not to set the flag to false.                 process_flag(trap_exit, true),                 MonRef = erlang:monitor(process, Parent),                 {Objs, Post, _LocalPost} =                      try                          Prep = prepare_qlc(H, not_a_list, GUnique,                                             GCache, TmpDir, MaxList),                         setup_qlc(Prep, Setup)                     catch Class:Reason ->                           Parent ! {self(), {caught, Class, Reason,                                      erlang:get_stacktrace()}},                           exit(normal)                     end,                 Parent ! {self(), ok},                 wait_for_request(Parent, MonRef, Post),                  reply(Parent, MonRef, Post, Objs)         end,    Pid = spawn_opt(CF, SpawnOptions),    parent_fun(Pid, Parent).%% Expect calls from tables calling the parent_fun and finally an 'ok'.parent_fun(Pid, Parent) ->    receive         {Pid, ok} -> Pid;        {TPid, {parent_fun, Fun}} ->            V = try                     {value, Fun()}                catch Class:Reason ->                    {parent_fun_caught, Class, Reason, erlang:get_stacktrace()}            end,            TPid ! {Parent, V},            parent_fun(Pid, Parent);        {Pid, {caught, throw, Error, [?THROWN_ERROR | _]}} ->            Error;        {Pid, {caught, Class, Reason, Stacktrace}} ->            erlang:raise(Class, Reason, Stacktrace);        _Ignored ->            parent_fun(Pid, Parent)    end.reply(Parent, MonRef, Post, []) ->    no_more(Parent, MonRef, Post);reply(Parent, MonRef, Post, [Answer | Cont]) ->    Parent ! {self(), {answer, Answer}},        wait_for_request(Parent, MonRef, Post),    reply(Parent, MonRef, Post, Cont);reply(Parent, MonRef, Post, Cont) ->    Reply = try                 if                     is_function(Cont) ->                        Cont();                    true ->                        throw_error(Cont)                end            catch                 Class:Reason ->                   post_funs(Post),                   Message = {caught, Class, Reason, erlang:get_stacktrace()},                   Parent ! {self(), Message},                   exit(normal)            end,    reply(Parent, MonRef, Post, Reply).no_more(Parent, MonRef, Post) ->    Parent ! {self(), no_more},    wait_for_request(Parent, MonRef, Post),    no_more(Parent, MonRef, Post).wait_for_request(Parent, MonRef, Post) ->    receive         {Parent, stop} ->            post_funs(Post),            exit(normal);        {Parent, more} ->            ok;        {'EXIT', Parent, _Reason} ->            post_funs(Post),            exit(normal);        {'DOWN', MonRef, process, Parent, _Info} ->            post_funs(Post),            exit(normal);        {'EXIT', Pid, _Reason} when Pid =:= self() ->            %% Trapped signal. The cursor ignores it...            wait_for_request(Parent, MonRef, Post);        Other ->            error_logger:error_msg(              "The QLC cursor ~w received an unexpected message:\n~p\n",               [self(), Other]),            wait_for_request(Parent, MonRef, Post)    end.%%% End of cursor process functions.%% Also in qlc_pt.erl.-define(Q, q).-define(QLC_Q(L1, L2, L3, L4, LC, Os),         {call,L1,{remote,L2,{atom,L3,?MODULE},{atom,L4,?Q}},[LC | Os]}).abstract(Info, false, NElements) ->    abstract(Info, NElements);abstract(Info, true, NElements) ->    Abstract = abstract(Info, NElements),    Vars = vars(Abstract),    {_, Body0, Expr} = flatten_abstr(Abstract, 1, Vars, []),    case Body0 of        [] ->             Expr;        [{match,_,Expr,Q}] ->            Q;        [{match,_,Expr,Q} | Body] ->            {block, 0, lists:reverse(Body, [Q])};        _ ->            {block, 0, lists:reverse(Body0, [Expr])}    end.abstract({qlc, E0, Qs0, Opt}, NElements) ->    Qs = lists:map(fun({generate, P, LE}) ->                           {generate, 1, binary_to_term(P),                             abstract(LE, NElements)};                      (F) ->                            binary_to_term(F)                   end, Qs0),    E = binary_to_term(E0),    Os = case Opt of             [] -> [];             _ -> [erl_parse:abstract(Opt, 1)]         end,    ?QLC_Q(1, 1, 1, 1, {lc,1,E,Qs}, Os);abstract({table, {M, F, As0}}, _NElements)                           when is_atom(M), is_atom(F), is_list(As0) ->    As = [erl_parse:abstract(A, 1) || A <- As0],    {call, 1, {remote, 1, {atom, 1, M}, {atom, 1, F}}, As};abstract({table, TableDesc}, _NElements) ->    case io_lib:deep_char_list(TableDesc) of        true ->            {ok, Tokens, _} = erl_scan:string(lists:flatten(TableDesc++".")),            {ok, [Expr]} = erl_parse:parse_exprs(Tokens),            Expr;        false -> % abstract expression            TableDesc    end;abstract({append, Infos}, NElements) ->    As = lists:foldr(fun(Info, As0) -> {cons,1,abstract(Info, NElements),As0}                     end, {nil, 1}, Infos),    {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, append}}, [As]};abstract({sort, Info, SortOptions}, NElements) ->        {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, sort}},     [abstract(Info, NElements), erl_parse:abstract(SortOptions, 1)]};abstract({keysort, Info, Kp, SortOptions}, NElements) ->        {call, 1, {remote, 1, {atom, 1, ?MODULE}, {atom, 1, keysort}},     [erl_parse:abstract(Kp, 1), abstract(Info, NElements),       erl_parse:abstract(SortOptions, 1)]};abstract({list,L,MS}, NElements) ->    {call, 1, {remote, 1, {atom, 1, ets}, {atom, 1, match_spec_run}},     [abstract(L, NElements),      {call, 1, {remote, 1, {atom, 1, ets}, {atom, 1, match_spec_compile}},       [erl_parse:abstract(MS, 1)]}]};abstract({list, L}, NElements) when NElements =:= infinity;                                     NElements >= length(L) ->    erl_parse:abstract(L, 1);abstract({list, L}, NElements) ->    erl_parse:abstract(lists:sublist(L, NElements) ++ more, 1).%% Since generator pattern variables cannot be used in list%% expressions, it is OK to flatten out QLC expressions using%% temporary variables.flatten_abstr(?QLC_Q(L1, L2, L3, L4, LC0, Os), VN0, Vars, Body0) ->    {lc,L,E,Qs0} = LC0,    F = fun({generate,Ln,P,LE0}, {VN1,Body1}) ->                {VN2,Body2,LE} = flatten_abstr(LE0, VN1, Vars, Body1),                {{generate,Ln,P,LE}, {VN2,Body2}};           (Fil, VN_Body) ->                {Fil, VN_Body}        end,    {Qs, {VN3,Body}} = lists:mapfoldl(F, {VN0,Body0}, Qs0),    LC = {lc,L,E,Qs},    {V, VN} = qlc_pt:aux_name1('V', VN3, Vars),    Var = {var, L1, V},    QLC = ?QLC_Q(L1, L2, L3, L4, LC, Os),    {VN + 1, [{match, L1, Var, QLC} | Body], Var};flatten_abstr(T0, VN0, Vars, Body0) when is_tuple(T0) ->    {VN, Body, L} = flatten_abstr(tuple_to_list(T0), VN0, Vars, Body0),    {VN, Body, list_to_tuple(L)};flatten_abstr([E0 | Es0], VN0, Vars, Body0) ->    {VN1, Body1, E} = flatten_abstr(E0, VN0, Vars, Body0),    {VN, Body, Es} = flatten_abstr(Es0, VN1, Vars, Body1),    {VN, Body, [E | Es]};flatten_abstr(E, VN, _Vars, Body) ->    {VN, Body, E}.vars(Abstract) ->    sets:from_list(ordsets:to_list(qlc_pt:vars(Abstract))).collect([], L) ->    lists:reverse(L);collect([Answer | Cont], L) ->    collect(Cont, [Answer | L]);collect(Cont, L) when is_function(Cont) ->    collect(Cont(), L);collect(Term, _L) ->    throw_error(Term).fold_loop(Fun, [Obj | Cont], Acc) ->    fold_loop(Fun, Cont, Fun(Obj, Acc));fold_loop(_Fun, [], Acc) ->    Acc;fold_loop(Fun, Cont, Acc) when is_function(Cont) ->    fold_loop(Fun, Cont(), Acc);fold_loop(_Fun, Term, _Acc) ->    throw_error(Term).next_loop(Pid, L, N) when N =/= 0 ->    case monitor_request(Pid, more) of        no_more ->            lists:reverse(L);        {answer, Answer} ->            next_loop(Pid, [Answer | L], N - 1);        {caught, throw, Error, [?THROWN_ERROR | _]} ->            Error;        {caught, Class, Reason, Stacktrace} ->            _ = (catch erlang:error(foo)),            erlang:raise(Class, Reason, Stacktrace ++ erlang:get_stacktrace());        error ->            erlang:error({qlc_cursor_pid_no_longer_exists, Pid})    end;next_loop(_Pid, L, _N) ->    lists:reverse(L).stop_cursor(Pid) ->    erlang:monitor(process, Pid),    unlink(Pid),    receive        {'EXIT',Pid,_Reason} -> % Simply ignore the error.            receive                 {'DOWN',_,process,Pid,_} -> ok            end    after 0 ->             Pid ! {self(),stop},            receive                {'DOWN',_,process,Pid,_} -> ok            end    end.monitor_request(Pid, Req) ->    Ref = erlang:monitor(process, Pid),    Pid ! {self(), Req},    receive         {'DOWN', Ref, process, Pid, _Info} ->            receive                {'EXIT', Pid, _Reason} -> ok            after 1 -> ok end,            error;        {'EXIT', Pid, _Reason} ->            receive                 {'DOWN', _, process, Pid, _} -> error            end;        {Pid, Reply} ->            erlang:demonitor(Ref),            receive                 {'DOWN', Ref, process, Pid, _Reason} -> Reply            after 0 -> Reply end    end.-define(TEMPLATE_STATE, 1).template_state() ->    ?TEMPLATE_STATE.%% Marker for skipped filter or unused generator.-define(SKIP, (-1)).%% Qual = {gen, LE} | fil-define(qual_data(QNum, GoToIndex, State, Qual),         {QNum, GoToIndex, State, Qual}).-record(join, {op, q1, q2, wh1, wh2, cs_fun}). % generated by qlc_pt%% le_info/1 returns an intermediate information format only used for%% testing purposes. Changes will happen without notice.%%%% QueryDesc = {qlc, TemplateDesc, [QualDesc], [QOpt]} %%           | {table, TableDesc}%%           | {append, [QueryDesc]}%%           | {sort, QueryDesc, [SortOption]}%%           | {keysort, KeyPos, QueryDesc, [SortOption]}%%           | {list, list()}%%           | {list, QueryDesc, MatchExpression}%% TableDesc = {Mod, Fun, Args}%%           | AbstractExpression%%           | character_list()%% Mod = module()%% Fun = atom()%% Args = [term()]%% QualDesc = FilterDesc%%          | {generate, PatternDesc, QueryDesc}%% QOpt = {cache, bool()} | cache %%      | {unique, bool()} | unique%% FilterDesc = PatternDesc = TemplateDesc = binary()le_info(#prepared{qh = #simple_qlc{le = LE, p = P, line = L, optz = Optz}}) ->    QVar = term_to_binary({var, L, P}),    {qlc, QVar, [{generate, QVar, le_info(LE)}], opt_info(Optz)};le_info(#prepared{qh = #qlc{codef = CodeF, qdata = Qdata, optz = Optz}}) ->    Code = CodeF(),    TemplateState = template_state(),    E = element(TemplateState, Code),    QualInfo0 = qual_info(Qdata, Code),    QualInfo1 = case Optz#optz.fast_join of                    #qlc_join{} = Join ->                        join_info(Join, QualInfo0, Qdata, Code);                    no ->                        QualInfo0                end,    QualInfo = [I || I <- QualInfo1, I =/= skip],    {qlc, E, QualInfo, opt_info(Optz)};le_info(#prepared{qh = #qlc_table{format_fun = FormatFun, trav_MS = TravMS,                                   ms = MS, lu_vals = LuVals}}) ->    case LuVals of        _ when FormatFun =:= undefined ->            {table, {'$MOD', '$FUN', []}};        {Pos, Vals} when MS =:= no_match_spec ->            {table, FormatFun({lookup, Pos, Vals})};        {Pos, Vals} ->            {list, {table, FormatFun({lookup, Pos, Vals})}, MS};        _ when TravMS, is_list(MS) ->            {table, FormatFun({match_spec, MS})};        _ when MS =:= no_match_spec ->            {table, FormatFun(all)}    end;le_info(#prepared{qh = #qlc_append{hl = HL}}) ->    {append, [le_info(H) || H <- HL]};le_info(#prepared{qh = #qlc_sort{h = H, keypos = sort,                                  opts = SortOptions0, tmpdir = TmpDir}}) ->    SortOptions = sort_options_global_tmp(SortOptions0, TmpDir),    {sort, le_info(H), SortOptions};le_info(#prepared{qh = #qlc_sort{h = H, keypos = {keysort, Kp},                                  opts = SortOptions0, tmpdir = TmpDir}}) ->    SortOptions = sort_options_global_tmp(SortOptions0, TmpDir),    {keysort, le_info(H), Kp, SortOptions};le_info(#prepared{qh = #qlc_list{l = L, ms = no_match_spec}}) ->    {list, L};le_info(#prepared{qh = #qlc_list{l = L, ms = MS}}) when is_list(L) ->    {list, {list, L}, MS};le_info(#prepared{qh = #qlc_list{l = L, ms = MS}}) ->

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?