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 + -
显示快捷键?