qlc.erl

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

ERL
1,743
字号
    {list, le_info(L), MS}.qual_info([?qual_data(_QNum, _GoI, ?SKIP, fil) | Qdata], Code) ->    %% see skip_lookup_filters()    [skip | qual_info(Qdata, Code)];qual_info([?qual_data(QNum, _GoI, _SI, fil) | Qdata], Code) ->    [element(QNum + 1, Code) | qual_info(Qdata, Code)];qual_info([?qual_data(_QNum, _GoI, _SI, {gen,#join{}}) | Qdata], Code) ->    [skip | qual_info(Qdata, Code)];qual_info([?qual_data(QNum, _GoI, _SI, {gen,LE}) | Qdata], Code) ->    [{generate,element(QNum + 1, Code),le_info(LE)} | qual_info(Qdata, Code)];qual_info([], _Code) ->    [].join_info(Join, QInfo, Qdata, Code) ->    #qlc_join{kind = Kind, q1 = QNum1a, c1 = C1, q2 = QNum2a, c2 = C2,               opt = Opt} = Join,    {?qual_data(JQNum,_,_,_), Rev, QNum1, QNum2, _WH1, _WH2, CsFun} =         find_join_data(Qdata, QNum1a, QNum2a),    {Cs1, Cs2, _Compat} = CsFun(),    L = 0,    G1_0 = {var,L,'G1'}, G2_0 = {var,L,'G2'},    JP = element(JQNum + 1, Code),    %% Create code for wh1 and wh2 in #join{}:    {{I1,G1}, {I2,G2}, QInfoL} =         if            Kind =:= merge ->                %% Create code for wh1 and wh2 in #join{}:                {JG1,QInfo1} = join_merge_info(QNum1, QInfo, Code, G1_0, Cs1),                {JG2,QInfo2} = join_merge_info(QNum2, QInfo, Code, G2_0, Cs2),                {JG1, JG2, QInfo1 ++ QInfo2};            Rev ->                {JG2,QInfo2} = join_merge_info(QNum2, QInfo, Code, G2_0, Cs2),                {J1, QInfo1} = join_lookup_info(QNum1, QInfo, G1_0),                {{J1,G1_0}, JG2, QInfo2 ++ [QInfo1]};            true ->                {JG1,QInfo1} = join_merge_info(QNum1, QInfo, Code, G1_0, Cs1),                {J2, QInfo2} = join_lookup_info(QNum2, QInfo, G2_0),                {JG1, {J2,G2_0}, QInfo1 ++ [QInfo2]}        end,    {JOptVal, JOp} = case Kind of                         merge -> {merge, '=='};                         {lookup, _} -> {lookup, '=:='}              end,    JOpt = [{join, JOptVal}] ++ opt_info(join_unique_cache(Opt)),    JFil = term_to_binary({op,L,JOp,                           {call,L,{atom,L,element},[{integer,L,C1},G1]},                           {call,L,{atom,L,element},[{integer,L,C2},G2]}}),    P = term_to_binary({cons, L, G1, G2}),    JInfo = {generate, JP, {qlc, P, QInfoL ++ [JFil], JOpt}},    {Before, [I1 | After]} = lists:split(QNum1 - 1, QInfo),    Before ++ [JInfo] ++ lists:delete(I2, After).%% qlc:q(P0 || P0 = Pattern <- H1, ConstFilters), %% where "P0" is a fresh variable and ConstFilters are filters that%% test constant values of pattern columns.join_merge_info(QNum, QInfo, Code, G, ExtraConstants) ->    {generate, _, LEInfo}=I = lists:nth(QNum, QInfo),    P = binary_to_term(element(QNum + 1, Code)),    case {P, ExtraConstants} of        {{var, _, _}, []} ->            %% No need to introduce a QLC expression.            {{I,P}, [I]};        _ ->             {EPV, M} =                 case P of                    {var, _, _} ->                         %% No need to introduce a pattern variable.                        {P, P};                    _ ->                         {PV, _} = qlc_pt:aux_name1('P', 0, vars(P)),                        L = 0,                        V = {var, L, PV},                        {V, {match, L, V, P}}                 end,            DQP = term_to_binary(EPV),            LEI = {generate, term_to_binary(M), LEInfo},            TP = term_to_binary(G),            CFs = [begin                       Call = {call,0,{atom,0,element},[{integer,0,Col},EPV]},                       F = list2op([{op,0,'=:=',erl_parse:abstract(Con),Call}                                      || Con <- Cs], 'or'),                       term_to_binary(F)                   end ||                      {Col,Cs} <- ExtraConstants],            {{I,G}, [{generate, TP, {qlc, DQP, [LEI | CFs], []}}]}    end.list2op([E], _Op) ->    E;list2op([E | Es], Op) ->    {op,0,Op,E,list2op(Es, Op)}.join_lookup_info(QNum, QInfo, G) ->    {generate, _, LEInfo}=I = lists:nth(QNum, QInfo),    TP = term_to_binary(G),    {I, {generate, TP, LEInfo}}.opt_info(#optz{unique = Unique, cache = Cache0, join_option = JoinOption}) ->    %% No 'nested_loop' options are added here, even if there are    %% nested loops to carry out, unless a 'nested_loop' was given as    %% option. The reason is that QLC does not know about all    %% instances of nested loops.    Cache = if                Cache0 -> ets;                true -> Cache0            end,    [{T,V} || {T,V} <- [{cache,Cache},{unique,Unique}],              V =/= default_option(T)] ++    [{T,V} || {T,V} <- [{join,JoinOption}], V =:= nested_loop].prepare_qlc(H, InitialValue, GUnique, GCache, TmpDir, MaxList) ->    GOpt = #qlc_opt{unique = GUnique, cache = GCache,                     tmpdir = TmpDir, max_list = MaxList},    case opt_le(prep_le(H, GOpt), 1) of        #prepared{qh = #qlc{} = QLC}=Prep ->            Prep#prepared{qh = QLC#qlc{init_value = InitialValue}};        #prepared{qh = #simple_qlc{}=SimpleQLC}=Prep ->            Prep#prepared{qh = SimpleQLC#simple_qlc{init_value = InitialValue}};        Prep ->            Prep    end.%%% The options given to append, q and table (unique and cache) as well%%% as the type of expression (list, table, append, qlc...) are%%% analyzed by prep_le. The results are is_unique_objects and%%% is_cached. List expressions are evaluated. prep_le(#qlc_lc{lc = LC_fun, opt = #qlc_opt{} = Opt0}=H, GOpt) ->    #qlc_opt{unique = GUnique, cache = GCache,              tmpdir = TmpDir, max_list = MaxList} = GOpt,    Unique = Opt0#qlc_opt.unique or GUnique,    Cache = if                not GCache -> Opt0#qlc_opt.cache;                true -> GCache            end,    Opt = Opt0#qlc_opt{unique = Unique, cache = Cache,                        tmpdir = TmpDir, max_list = MaxList},    prep_qlc_lc(LC_fun(), Opt, GOpt, H);prep_le(#qlc_table{info_fun = IF}=T, GOpt) ->    {SortInfo, Sorted} = table_sort_info(T),    IsUnique = grd(IF, is_unique_objects),    Prep = #prepared{qh = T, sort_info = SortInfo, sorted = Sorted,                     is_unique_objects = IsUnique},    Opt = if               IsUnique or not GOpt#qlc_opt.unique,              T#qlc_table.ms =:= no_match_spec ->                   GOpt#qlc_opt{cache = false};              true ->                  GOpt          end,    may_create_simple(Opt, Prep);prep_le(#qlc_append{hl = HL}, GOpt) ->    case lists:flatmap(fun(#prepared{qh = #qlc_list{l = []}}) -> [];                          (#prepared{qh = #qlc_append{hl = HL1}}) -> HL1;                          (H) -> [H] end,                        [prep_le(H, GOpt) || H <- HL]) of        []=Nil ->             short_list(Nil);        [Prep] ->             Prep;        PrepL ->             Cache = lists:all(fun(#prepared{is_cached = IM}) -> IM =/= false                              end, PrepL),            %% The handles in hl are replaced by prepared handles:            Prep = #prepared{qh = #qlc_append{hl = PrepL}, is_cached = Cache},            may_create_simple(GOpt, Prep)    end;prep_le(#qlc_sort{h = H0}=Q0, GOpt) ->    %% The handle h is replaced by a prepared handle:    Q = Q0#qlc_sort{h = prep_le(H0, GOpt)},    prep_sort(Q, GOpt);prep_le([_, _ | _]=L, GOpt) ->    Prep = #prepared{qh = #qlc_list{l = L}, is_cached = true},    Opt = if               not GOpt#qlc_opt.unique ->                   GOpt#qlc_opt{cache = false};              true -> GOpt           end,    may_create_simple(Opt, Prep);prep_le(L, _GOpt) when is_list(L) ->    short_list(L);prep_le(T, _GOpt) ->    erlang:error({unsupported_qlc_handle, #qlc_handle{h = T}}).eval_le(LE_fun, GOpt) ->    case LE_fun() of        {error, ?MODULE, _} = Error ->            throw_error(Error);        R ->             case get_handle(R) of                badarg ->                    erlang:error(badarg, [R]);                H ->                     prep_le(H, GOpt)            end    end.prep_qlc_lc({simple_v1, PVar, LE_fun, L}, Opt, GOpt, _H) ->    check_lookup_option(Opt, false),    prep_simple_qlc(PVar, L, eval_le(LE_fun, GOpt), Opt);prep_qlc_lc({single_v1, QFun, CodeF, Qdata0, _, MS, PosFun}, Opt, GOpt, _H) ->    %% R10B    check_join_option(Opt),    %% Filter optional:    [?qual_data(QNum, GoI_G, SI_G, {gen, LE_fun}) | Filter] = Qdata0,     Prep0 = eval_le(LE_fun, GOpt),    Fs = [0], % (effect: the match specification is always run)    {Done, _Skip, LookUp, Prep} = prep_gen(Prep0, PosFun, {MS,Fs}, Opt),    check_lookup_option(Opt, LookUp),    if        Done =:= replace ->            Prep;        true ->            Qdata = [?qual_data(QNum, GoI_G, SI_G, {gen, Prep}) | Filter],            QOpt = undefined,            prep_qlc(QFun, CodeF, Qdata, QOpt, Opt)    end;prep_qlc_lc({qlc_v1, QFun, CodeF, Qdata0, QOpt}, Opt, GOpt, _H) ->    F = fun(?qual_data(_QNum, _GoI, _SI, fil)=QualData, ModGens) ->                 {QualData, ModGens};           (?qual_data(_QNum, _GoI, _SI, {gen, #join{}})=QualData, ModGens) ->                {QualData, ModGens};           (?qual_data(QNum, GoI, SI, {gen, LE_fun}), ModGens0) ->                 Prep1 = eval_le(LE_fun, GOpt),                {Prep, ModGens} =                     prep_generator(QNum, Prep1, QOpt, Opt, ModGens0),                {?qual_data(QNum, GoI, SI, {gen, Prep}), ModGens}        end,    {Qdata, ModGens} = lists:mapfoldl(F, [], Qdata0),    SomeLookUp = lists:keysearch(true, 2, ModGens) =/= false,    check_lookup_option(Opt, SomeLookUp),    case ModGens of        [{_QNum, _LookUp, all, OnePrep}] ->            check_join_option(Opt),            OnePrep;        _ ->             Prep0 = prep_qlc(QFun, CodeF, Qdata, QOpt, Opt),            SkipFs = lists:flatmap(fun({_QNum,_LookUp,Fs,_Prep}) -> Fs                                    end, ModGens),            Prep1 = skip_lookup_filters(Prep0, SkipFs),            prep_join(Prep1, QOpt, Opt)    end;prep_qlc_lc(_, _Opt, _GOpt, H) ->    erlang:error({unsupported_qlc_handle, #qlc_handle{h = H}}).prep_generator(QNum, Prep0, QOpt, Opt, ModGens) ->    PosFun = constants(QOpt, QNum),    MSFs = case match_specs(QOpt, QNum) of               undefined ->                   {no_match_spec, []};               {_, _}=MSFs0 ->                   MSFs0         end,    case prep_gen(Prep0, PosFun, MSFs, Opt) of        {replace, Fs, LookUp, Prep} ->            {Prep, [{QNum,LookUp,Fs,Prep} | ModGens]};        {skip, SkipFils, LookUp, Prep} ->            {Prep, [{QNum,LookUp,SkipFils,Prep} | ModGens]};        {no, _Fs, _LookUp, Prep} ->            {Prep, ModGens}    end.prep_gen(#prepared{qh = LE0}=Prep0, PosFun, {MS, Fs}, Opt) ->    {LuV, {STag,SkipFils}} = find_const_positions(LE0, PosFun, Opt),    LU = LuV =/= false,    case LE0 of        #qlc_table{lu_vals = LuV0, ms = MS0} when LuV0 =/= undefined;                                                   MS0 =/= no_match_spec ->            {no, [], false, Prep0};        #qlc_table{} when MS =/= no_match_spec, LU ->            MS1 = if                       Fs =:= SkipFils; STag =:= Fs ->                          %% The guard of the match specification                           %% is covered by the lookup.                          case MS of                              [{'$1',_Guard,['$1']}] -> % no transformation                                  no_match_spec;                              [{Head,_Guard,Body}] ->                                  [{Head,[],Body}] % true guard                          end;                      true ->                          MS                  end,            Prep = Prep0#prepared{qh = LE0#qlc_table{lu_vals = LuV,ms = MS1}},            {replace, Fs, LU, Prep};        #qlc_table{} when LU ->            Prep = Prep0#prepared{qh = LE0#qlc_table{lu_vals = LuV}},            {skip, SkipFils, LU, Prep};        #qlc_table{trav_MS = true} when MS =/= no_match_spec ->            Prep = Prep0#prepared{qh = LE0#qlc_table{ms = MS}},            {replace, Fs, false, may_create_simple(Opt, Prep)};        #qlc_list{l = []} -> % unique and cached            {replace, Fs, false, Prep0};        #qlc_list{ms = no_match_spec} when MS =/= no_match_spec ->            Prep = Prep0#prepared{qh = LE0#qlc_list{ms = MS},                                   is_cached = false},            {replace, Fs, false, may_create_simple(Opt, Prep)};        #qlc_list{} when MS =/= no_match_spec ->            ListMS = #qlc_list{l = Prep0, ms = MS},            LE = #prepared{qh = ListMS, is_cached = false},            {replace, Fs, false, may_create_simple(Opt, LE)};        _ ->            {no, [], false, Prep0}    end.-define(SIMPLE_QVAR, 'SQV').may_create_simple(#qlc_opt{unique = Unique, cache = Cache} = Opt,                   #prepared{is_cached = IsCached,                             is_unique_objects = IsUnique} = Prep) ->    if         Unique and not IsUnique; (Cache =/= false) and not IsCached ->            prep_simple_qlc(?SIMPLE_QVAR, 1, Prep, Opt);        true ->            Prep    end.%% Sorted. Var kommer den ifr錸? P鍁erkas den av unique&cache=list?prep_simple_qlc(PVar, Line, LE, Opt) ->    check_join_option(Opt),    #prepared{is_cached = IsCached,               sort_info = SortInfo, sorted = Sorted,              is_unique_objects = IsUnique} = LE,    #qlc_opt{unique = Unique, cache = Cache} = Opt,    Cachez = if                 Unique -> Cache;                 not IsCached -> Cache;                 true -> false             end,    Optz = #optz{unique = Unique and not IsUnique,                  cache = Cachez, opt = Opt},    QLC = #simple_qlc{p = PVar, le = LE, line = Line,                       init_value = not_a_list, optz = Optz},    %% LE#prepared.join is not copied    #prepared{qh = QLC, is_unique_objects = IsUnique or Unique,               sort_info = SortInfo, sorted = Sorted,              is_cached = IsCached or (Cachez =/= false)}.prep_sort(#qlc_sort{h = #prepared{sorted = yes}=Prep}, _Opt) ->    Prep;prep_sort(#qlc_sort{h = #prepared{is_unique_objects = IsUniqueObjs}}=Q, Opt) ->    S1 = sort_unique(IsUniqueObjs, Q),    S = sort_tmpdir(S1, Opt),    {SortInfo, Sorted} = sort_sort_info(S),    #prepared{qh = S, is_cached = true, sort_info = SortInfo,              sorted = Sorted,              is_unique_objects = S#qlc_sort.unique or IsUniqueObjs}.

⌨️ 快捷键说明

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