qlc.erl

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

ERL
1,743
字号
prep_qlc(QFun, CodeF, Qdata0, QOpt, Opt) ->    #qlc_opt{unique = Unique, cache = Cache, join = Join} = Opt,    Optz = #optz{unique = Unique, cache = Cache,                  join_option = Join, opt = Opt},    {Qdata, SortInfo} = qlc_sort_info(Qdata0, QOpt),    QLC = #qlc{lcf = QFun, codef = CodeF, qdata = Qdata,                init_value = not_a_list, optz = Optz},    #prepared{qh = QLC, sort_info = SortInfo,              is_unique_objects = Unique,               is_cached = Cache =/= false}.%% 'sorted', 'sorted_info', and 'sorted_info2' are used to avoid%% sorting on a key when there is no need to sort on the key. 'sorted'%% is set by qlc:sort() only; its purpose is to assure that if columns%% 1 to i are constant, then column i+1 is key-sorted (always true if%% the tuples are sorted). Note: the implementation is (too?) simple.%% For instance, each column is annotated with 'ascending' or%% 'descending' (not yet). More exact would be, as examples, 'always%% ascending' and 'ascending if all preceding columns are constant'.%%%% The 'size' of the template is not used (size_of_qualifier(QOpt, 0)).qlc_sort_info(Qdata, undefined) -> % single_v1, R10B    F = fun(?qual_data(QNum, GoI, SI,                        {gen, #prepared{sort_info = SI}=Prep})) ->                NPrepLE = Prep#prepared{sort_info2 = SI},                ?qual_data(QNum, GoI, SI, {gen, NPrepLE});           (Qd) -> Qd        end,    {lists:map(F, Qdata), []};qlc_sort_info(Qdata0, QOpt) ->    F = fun(?qual_data(_QNum, _GoI, _SI, fil)=Qd, Info) ->                 {Qd, Info};           (?qual_data(_QNum, _GoI, _SI, {gen, #join{}})=Qd, Info) ->                {Qd, Info};           (?qual_data(QNum, GoI, SI, {gen, PrepLE0}), Info) ->                 PrepLE = sort_info(PrepLE0, QNum, QOpt),                Qd = ?qual_data(QNum, GoI, SI, {gen, PrepLE}),                I = [{{Column,Order}, [{traverse,QNum,C}]} ||                        {{C,Order},What} <- PrepLE#prepared.sort_info2,                        What =:= [], % Something else later...                        Column <- equal_template_columns(QOpt, {QNum,C})],                {Qd, [I | Info]}        end,    {Qdata, SortInfoL} = lists:mapfoldl(F, [], Qdata0),    SortInfo0 = [{{Pos,Ord}, [template]} ||                     Pos <- constant_columns(QOpt, 0),                     Ord <- orders(yes)]           ++ lists:append(SortInfoL),    SortInfo = family_union(SortInfo0),    {Qdata, SortInfo}.sort_info(#prepared{sort_info = SI, sorted = S} = Prep, QNum, QOpt) ->    SI1 = [{{C,Ord},[]} ||               S =/= no,               is_integer(Sz = size_of_qualifier(QOpt, QNum)),               Sz > 0, % the size of the pattern              (NConstCols = size_of_constant_prefix(QOpt, QNum)) < Sz,               C <- [NConstCols+1],              Ord <- orders(S)]       ++ [{{Pos,Ord},[]} || Pos <- constant_columns(QOpt, QNum),                             Ord <- orders(yes)]       ++ [{PosOrd,[]} || {PosOrd,_} <- SI],    SI2 = lists:usort(SI1),    Prep#prepared{sort_info2 = SI2}. %orders(descending=O) ->%    [O];orders(ascending=O) ->    [O];orders(yes) ->    [ascending%   ,descending    ].sort_unique(true, #qlc_sort{opts = SortOptions, keypos = sort}=Sort) ->    Sort#qlc_sort{unique = false,                   opts = lists:keydelete(unique,                                          1,                                          lists:delete(unique, SortOptions))};sort_unique(_, Sort) ->    Sort.sort_tmpdir(S, #qlc_opt{tmpdir = ""}) ->    S;sort_tmpdir(S, Opt) ->    S#qlc_sort{tmpdir = Opt#qlc_opt.tmpdir}.short_list(L) ->    %% length(L) < 2: all elements are known be equal    #prepared{qh = #qlc_list{l = L}, sorted = yes, is_unique_objects = true,               is_cached = true}.    find_const_positions(#qlc_table{info_fun = IF, lookup_fun = LU_fun},                      PosFun, #qlc_opt{max_lookup = Max, lookup = Lookup})            when is_function(LU_fun), is_function(PosFun), is_function(IF),                Lookup =/= false ->    case call(IF, keypos, undefined, [])  of        undefined ->            Indices = call(IF, indices, undefined, []),            find_const_position_idx(Indices, PosFun, Max, []);        KeyPos ->            case pos_vals(KeyPos, PosFun(KeyPos), Max) of                false ->                    find_const_position_idx(IF(indices), PosFun, Max, []);                PosValuesSkip ->                    PosValuesSkip            end    end;find_const_positions(_, _PosFun, _Opt0) ->    {false, {some,[]}}.find_const_position_idx([I | Is], PosFun, Max, L0) ->    case pos_vals(I, PosFun(I), Max) of        false ->            find_const_position_idx(Is, PosFun, Max, L0);        {{_Pos, Values}, _SkipFils}=PosValuesFils ->            L = [{length(Values), PosValuesFils} | L0],            find_const_position_idx(Is, PosFun, Max, L)    end;find_const_position_idx(_, _PosFun, _Max, []) ->    {false, {some,[]}};find_const_position_idx(_, _PosFun, _Max, L) ->    [{_,PVF} | _] = lists:sort(L),    PVF.pos_vals(Pos, {usort_needed, Values, SkipFils}, Max) ->    pos_vals_max(Pos, lists:usort(Values), SkipFils, Max);pos_vals(Pos, {values, Values, SkipFils}, Max) ->    pos_vals_max(Pos, Values, SkipFils, Max);pos_vals(Pos, {Tag, Values}, Max) ->    %% R10B    pos_vals(Pos, {Tag, Values, {some,[]}}, Max);pos_vals(_Pos, _, _Max) ->    false.%% length(Values) >= 1pos_vals_max(Pos, Values, Skip, Max) when Max =:= -1; Max >= length(Values) ->    {{Pos, Values}, Skip};pos_vals_max(_Pos, _Value, _Skip, _Max) ->    false.skip_lookup_filters(Prep, []) ->    Prep;skip_lookup_filters(#prepared{qh = #qlc{qdata = Qdata0}=QLC}=Prep, SkipFs) ->    Qdata = [case lists:member(QNum, SkipFs) of                 true ->                     ?qual_data(QNum, GoI, ?SKIP, fil);                 false ->                     Qd             end || ?qual_data(QNum, GoI, _, _)=Qd <- Qdata0],    Prep#prepared{qh = QLC#qlc{qdata = Qdata}}.prep_join(Prep, QOpt, Opt) ->    case join_opt(QOpt) of        undefined ->            check_join_option(Opt),            Prep;        EqualMatch ->            {Ix, M} = case EqualMatch of                          {Equal, Match} ->                              {Ix0, _} = pjoin(Match, Prep, QOpt, Opt),                              {_, M0} = pjoin(Equal, Prep, QOpt, Opt),                              {Ix0, M0};                          _ ->                              pjoin(EqualMatch, Prep, QOpt, Opt)                      end,            SI = family_union(Prep#prepared.sort_info ++ M),            Prep#prepared{join = {Ix, M}, sort_info = SI}    end.%% The parse transform ensures that only two tables are involved.pjoin(QCsL, #prepared{qh = #qlc{qdata = QData}}, QOpt,       #qlc_opt{join = JoinOpt}) ->    LuJoin = (JoinOpt =:= any) or (JoinOpt =:= lookup),    MJoin = (JoinOpt =:= any) or (JoinOpt =:= merge),    lists:foldl(fun(QCs, {Ix0, M0}) ->                         {Ix,M} = pref_join(QCs, QData, QOpt, LuJoin, MJoin),                        {Ix0++Ix, M0++M}                end, {[],[]}, QCsL).pref_join([{Q1,Cs1},{Q2,Cs2}], QData, QOpt, LuJoin, MJoin) ->    {Is1, Sort1} = join_qual_data(QData, Q1),    {Is2, Sort2} = join_qual_data(QData, Q2),    Lu1 = [pref_lookup(Q1, C1, Q2, C2, QOpt) ||              LuJoin, C2 <- Is2, lists:member(C2, Cs2), C1 <- Cs1],    Lu2 = [pref_lookup(Q2, C2, Q1, C1, QOpt) ||              LuJoin, C1 <- Is1, lists:member(C1, Cs1), C2 <- Cs2],    Merge = lists:append([pref_merge(Q1, C1, Q2, C2, Sort1, Sort2, QOpt) ||                             MJoin, C1 <- Cs1, C2 <- Cs2]),    {family(Lu1 ++ Lu2), family_union(Merge)}.pref_lookup(Q1, C1, Q2, C2, QOpt) ->    {{Q1,C1,Q2,C2},{lookup,eq_template_columns(QOpt, {Q1,C1})}}.pref_merge(Q1, C1, Q2, C2, Sort1, Sort2, QOpt) ->    Col1 = {Q1,C1},    Col2 = {Q2,C2},    DoSort = [QC || {{_QNum,Col}=QC,SortL} <- [{Col1,Sort1}, {Col2,Sort2}],                    lists:keysearch({Col, ascending}, 1, SortL) =:= false],    J = [{{Q1,C1,Q2,C2}, {merge,DoSort}}],    %% true = (QOpt(template))(Col1, '==') =:= (QOpt(template))(Col2, '==')    [{{Column, ascending}, J} ||         Column <- equal_template_columns(QOpt, Col1)] ++ [{other, J}].join_qual_data(QData, QNum) ->    case lists:keysearch(QNum, 1, QData) of        {value, ?qual_data(QNum, _, _, {gen, PrepLE})} ->            #prepared{sort_info2 = SortInfo} = PrepLE,            {join_indices(PrepLE), SortInfo}    end.join_indices(#prepared{qh = #qlc_table{info_fun = IF,                                        ms = no_match_spec,                                       lu_vals = undefined}}) ->    KpL = case call(IF, keypos, undefined, []) of              undefined -> [];              Kp -> [Kp]          end,    case call(IF, indices, undefined, []) of        undefined -> KpL;        Is0 -> KpL ++ Is0    end;join_indices(_Prep) ->    [].table_sort_info(#qlc_table{info_fun = IF}) ->    case call(IF, is_sorted_key, undefined, []) of        undefined ->             {[], no};        false ->             {[], no};        true ->             case call(IF, keypos, undefined, []) of                undefined -> % strange                    {[], no};                KeyPos ->                    {[{{KeyPos,ascending},[]}], no}            end    end.sort_sort_info(#qlc_sort{keypos = sort, order = Ord0}) ->    {[], sort_order(Ord0)};sort_sort_info(#qlc_sort{keypos = {keysort,Kp0}, order = Ord0}) ->    Kp = case Kp0 of             [Pos | _] -> Pos;             _ -> Kp0         end,    {[{{Kp,sort_order(Ord0)},[]}], no}.sort_order(F) when is_function(F) ->    no;sort_order(Order) ->    Order.check_join_option(#qlc_opt{join = any}) ->    ok;check_join_option(#qlc_opt{join = Join}) ->    erlang:error(no_join_to_carry_out, [{join,Join}]).check_lookup_option(#qlc_opt{lookup = true}, false) ->    erlang:error(no_lookup_to_carry_out, [{lookup,true}]);check_lookup_option(_QOpt, _LuV) ->    ok.equal_template_columns(QOpt, QNumColumn) ->    (QOpt(template))(QNumColumn, '==').eq_template_columns(QOpt, QNumColumn) ->    (QOpt(template))(QNumColumn, '=:=').size_of_constant_prefix(QOpt, QNum) ->    (QOpt(n_leading_constant_columns))(QNum).constants(undefined, _QNum) ->    no_column_fun;constants(QOpt, QNum) ->    (QOpt(constants))(QNum).join_opt(undefined=U) ->    U;join_opt(QOpt) ->    QOpt(join).match_specs(undefined=U, _QNum) ->    U;match_specs(QOpt, QNum) ->    (QOpt(match_specs))(QNum).constant_columns(QOpt, QNum) ->    (QOpt(constant_columns))(QNum).size_of_qualifier(QOpt, QNum) ->    (QOpt(size))(QNum).%% Two optimizations are carried out:%% 1. The first generator is never cached if the QLC expression itself%% is cached. Since the answers do not need to be cached, the top-most%% QLC expression is never cached either. Simple QLCs not holding any%% options are removed. Simple QLCs are coalesced when possible.%% 2. Merge join and lookup join is done if possible.opt_le(#prepared{qh = #simple_qlc{le = LE0, optz = Optz0}=QLC}=Prep0,        GenNum) ->    case LE0 of        #prepared{qh = #simple_qlc{p = LE_Pvar, le = LE2, optz = Optz2}} ->            %% Coalesce two simple QLC expressions.            Cachez = case Optz2#optz.cache of                         false -> Optz0#optz.cache;                         Cache2 -> Cache2                     end,            Optz = Optz0#optz{cache = Cachez,                              unique = Optz0#optz.unique or Optz2#optz.unique},            PVar = if                        LE_Pvar =:= ?SIMPLE_QVAR -> QLC#simple_qlc.p;                       true -> LE_Pvar                   end,            Prep = Prep0#prepared{qh = QLC#simple_qlc{p = PVar, le = LE2,                                                      optz = Optz}},            opt_le(Prep, GenNum);        _ ->            Optz1 = no_cache_of_first_generator(Optz0, GenNum),            case {opt_le(LE0, 1), Optz1} of                {LE, #optz{unique = false, cache = false}} ->                    LE;                {LE, _} ->                    Prep0#prepared{qh = QLC#simple_qlc{le = LE, optz = Optz1}}            end    end;opt_le(#prepared{qh = #qlc{qdata = Qdata0, optz = Optz0}=QLC}=Prep, GenNum) ->    #optz{join_option = JoinOption, opt = Opt} = Optz0,    JoinOption = Optz0#optz.join_option,    {Join, DoSort} =         opt_join(Prep#prepared.join, JoinOption, Qdata0, Opt),    F = fun(?qual_data(QNum, GoI, SI, {gen, #prepared{}=PrepLE}), GenNum1) ->                NewPrepLE = maybe_sort(PrepLE, QNum, DoSort, Opt),                {?qual_data(QNum, GoI, SI, {gen, opt_le(NewPrepLE, GenNum1)}),                 GenNum1 + 1};           (Qd, GenNum1) ->                {Qd, GenNum1}        end,    {Qdata, _} = lists:mapfoldl(F, 1, Qdata0),    Optz1 = no_cache_of_first_generator(Optz0, GenNum),    Optz = Optz1#optz{fast_join = Join},    Prep#prepared{qh = QLC#qlc{qdata = Qdata, optz = Optz}};opt_le(#prepared{qh = #qlc_append{hl = HL}}=Prep, GenNum) ->    Hs = [opt_le(

⌨️ 快捷键说明

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