qlc.erl

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

ERL
1,743
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id $%%-module(qlc).%%% Purpose: Main API module QLC. Functions for evaluation.%%% Other files:%%% qlc_pt. Implements the parse transform.%% External exports -export([parse_transform/2, transform_from_evaluator/2]).-export([q/1, q/2]).-export([eval/1, e/1, eval/2, e/2, fold/3, fold/4]).-export([cursor/1, cursor/2,          next_answers/1, next_answers/2,          delete_cursor/1]).-export([append/1, append/2]).-export([sort/1, sort/2, keysort/2, keysort/3]).-export([table/2]).-export([info/1, info/2]).-export([string_to_handle/1, string_to_handle/2, string_to_handle/3]).-export([format_error/1]).%% Exported to qlc_pt.erl only:-export([template_state/0]).%% When cache=list lists bigger than ?MAX_LIST_SIZE bytes are put on%% file. Also used when merge join finds big equivalence classes.-define(MAX_LIST_SIZE, 512*1024).-record(qlc_append, % qlc:append/1,2        {hl        }).-record(qlc_table,   % qlc:table/2        {trav_fun,   % traverse fun         trav_MS,    % bool(); true iff traverse fun takes a match spec         pre_fun,         post_fun,         info_fun,         format_fun,         lookup_fun,         parent_fun,         f1,         % unused         lu_vals,    % undefined | {Position,Values}; values to be looked up         ms = no_match_spec                     % match specification; [T || P <- Tab, Fs]        }).-record(qlc_sort,   % qlc:sort/1,2 and qlc:keysort/2,3        {h,         keypos,    % sort | {keysort, KeyPos}         unique,         compressed,         order,         opts,         tmpdir        }).%% Also in qlc_pt.erl.-record(qlc_lc,     % qlc:q/1,2        {lc,         opt        % #qlc_opt        }).-record(qlc_list,   % a prepared list        {l,         ms = no_match_spec        }).-record(qlc_join,        % a prepared join        {kind,           % merge | {lookup, LookupFun}         opt,            % #qlc_opt from q/2.         h1, q1, c1,     % to be traversed by "lookup join"         h2, q2, c2      % to be looked up by "lookup join"        }).%%% A query cursor is a tuple {qlc_cursor, Cursor} where Cursor is a pair%%% {CursorPid, OwnerPid}.-record(qlc_cursor, {c}).-record(qlc_opt,        {unique = false,      % bool()         cache = false,       % bool() | list (true~ets, false~no)         max_lookup = -1,     % int() >= 0 | -1 (represents infinity)         join = any,          % any | nested_loop | merge | lookup         tmpdir = "",         % global tmpdir         lookup = any,        % any | bool()         max_list = ?MAX_LIST_SIZE  % int() >= 0        }).-record(setup, {parent}).-define(THROWN_ERROR, {?MODULE, throw_error, _}).%%% A query handle is a tuple {qlc_handle, Handle} where Handle is one%%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc.-record(qlc_handle, {h}).get_handle(#qlc_handle{h = #qlc_lc{opt = {qlc_opt, U, C, M}}=H}) ->    %% R10B and R11B-0.    H#qlc_lc{opt = #qlc_opt{unique = U, cache = C, max_lookup = M}};get_handle(#qlc_handle{h = H}) ->    H;get_handle(L) when is_list(L) ->    L;get_handle(_) ->    badarg.%%%%%% Exported functions%%%append(QHs) ->    Hs = [case get_handle(QH) of              badarg -> erlang:error(badarg, [QHs]);              H -> H          end || QH <- QHs],    #qlc_handle{h = #qlc_append{hl = Hs}}.append(QH1, QH2) ->    Hs = [case get_handle(QH) of              badarg -> erlang:error(badarg, [QH1, QH2]);              H -> H          end || QH <- [QH1, QH2]],    #qlc_handle{h = #qlc_append{hl = Hs}}.cursor(QH) ->    cursor(QH, []).cursor(QH, Options) ->    case {options(Options, [unique_all, cache_all, tmpdir,                             spawn_options, max_list_size]),          get_handle(QH)} of        {B1, B2} when B1 =:= badarg; B2 =:= badarg ->            erlang:error(badarg, [QH, Options]);        {[GUnique, GCache, TmpDir, SpawnOptions0, MaxList], H} ->            SpawnOptions = spawn_options(SpawnOptions0),            case cursor_process(H, GUnique, GCache, TmpDir,                                 SpawnOptions, MaxList) of                Pid when is_pid(Pid) ->                    #qlc_cursor{c = {Pid, self()}};                Error ->                    Error            end    end.delete_cursor(#qlc_cursor{c = {_, Owner}}=C) when Owner =/= self() ->    erlang:error(not_cursor_owner, [C]);delete_cursor(#qlc_cursor{c = {Pid, _}}) ->    stop_cursor(Pid);delete_cursor(T) ->    erlang:error(badarg, [T]).e(QH) ->    eval(QH, []).e(QH, Options) ->    eval(QH, Options).eval(QH) ->    eval(QH, []).eval(QH, Options) ->    case {options(Options, [unique_all, cache_all, tmpdir, max_list_size]),          get_handle(QH)} of        {B1, B2} when B1 =:= badarg; B2 =:= badarg ->            erlang:error(badarg, [QH, Options]);        {[GUnique, GCache, TmpDir, MaxList], Handle} ->            try                 Prep = prepare_qlc(Handle, [], GUnique, GCache,                                    TmpDir, MaxList),                case setup_qlc(Prep, #setup{parent = self()}) of                    {L, Post, _LocalPost} when is_list(L) ->                        post_funs(Post),                        L;                    {Objs, Post, _LocalPost} when is_function(Objs) ->                        try                            (ensure_collecting(Prep, Objs))()                        after                            post_funs(Post)                        end                end            catch Term ->                case erlang:get_stacktrace() of                    [?THROWN_ERROR | _] ->                        Term;                    Stacktrace ->                        erlang:raise(throw, Term, Stacktrace)                end            end    end.fold(Fun, Acc0, QH) ->    fold(Fun, Acc0, QH, []).fold(Fun, Acc0, QH, Options) ->    case {options(Options, [unique_all, cache_all, tmpdir, max_list_size]),           get_handle(QH)} of        {B1, B2} when B1 =:= badarg; B2 =:= badarg ->            erlang:error(badarg, [Fun, Acc0, QH, Options]);        {[GUnique, GCache, TmpDir, MaxList], Handle} ->            try                Prep = prepare_qlc(Handle, not_a_list, GUnique, GCache,                                   TmpDir, MaxList),                case setup_qlc(Prep, #setup{parent = self()}) of                    {Objs, Post, _LocalPost} when is_function(Objs);                                                   is_list(Objs) ->                        try                            fold_loop(Fun, Objs, Acc0)                        after                            post_funs(Post)                        end                end            catch Term ->                case erlang:get_stacktrace() of                    [?THROWN_ERROR | _] ->                        Term;                    Stacktrace ->                        erlang:raise(throw, Term, Stacktrace)                end            end    end.format_error(not_a_query_list_comprehension) ->    io_lib:format("argument is not a query list comprehension", []);format_error({used_generator_variable, V}) ->    io_lib:format("generated variable ~w must not be used in list expression",                  [V]);format_error(binary_generator) ->    io_lib:format("cannot handle binary generators", []);format_error(too_complex_join) ->    io_lib:format("cannot handle join of three or more generators efficiently",                  []);format_error(too_many_joins) ->    io_lib:format("cannot handle more than one join efficiently", []);format_error({Line, Mod, Reason}) when is_integer(Line) ->    io_lib:format("~p: ~s~n",                   [Line, lists:flatten(Mod:format_error(Reason))]);%% file_sorter errorsformat_error({bad_object, FileName}) ->    io_lib:format("the temporary file \"~s\" holding answers is corrupt",                 [FileName]);format_error(bad_object) ->    io_lib:format("the keys could not be extracted from some term", []);format_error({file_error, FileName, Reason}) ->    io_lib:format("\"~s\": ~p~n",[FileName, file:format_error(Reason)]);format_error({premature_eof, FileName}) ->    io_lib:format("\"~s\": end-of-file was encountered inside some binary term",                   [FileName]);format_error({error, Module, Reason}) ->    Module:format_error(Reason);format_error(E) ->    io_lib:format("~p~n", [E]).info(QH) ->    info(QH, []).info(QH, Options) ->    case {options(Options, [unique_all, cache_all, flat, format, n_elements,                             tmpdir, max_list_size]),          get_handle(QH)} of        {B1, B2} when B1 =:= badarg; B2 =:= badarg ->            erlang:error(badarg, [QH, Options]);        {[GUnique, GCache, Flat, Format, NElements, TmpDir, MaxList], H} ->            try                Prep = prepare_qlc(H, [], GUnique, GCache, TmpDir, MaxList),                Info = le_info(Prep),                AbstractCode = abstract(Info, Flat, NElements),                case Format of                    abstract_code ->                        AbstractCode;                    string ->                        lists:flatten(erl_pp:expr(AbstractCode, 0, none));                    debug -> % Not documented. Intended for testing only.                        Info                end            catch Term ->                case erlang:get_stacktrace() of                    [?THROWN_ERROR | _] ->                        Term;                    Stacktrace ->                        erlang:raise(throw, Term, Stacktrace)                end            end    end.keysort(KeyPos, QH) ->    keysort(KeyPos, QH, []).keysort(KeyPos, QH, Options) ->    case {is_keypos(KeyPos),           options(Options, [tmpdir, order, unique, compressed,                             size, no_files]),          get_handle(QH)} of        {true, [TmpDir, Order, Unique,Compressed | _], H} when H =/= badarg ->            #qlc_handle{h = #qlc_sort{h = H, keypos = {keysort,KeyPos},                                       unique = Unique, compressed = Compressed,                                      order = Order, opts = listify(Options),                                       tmpdir = TmpDir}};        _ ->            erlang:error(badarg, [KeyPos, QH, Options])    end.-define(DEFAULT_NUM_OF_ANSWERS, 10).next_answers(C) ->    next_answers(C, ?DEFAULT_NUM_OF_ANSWERS).next_answers(#qlc_cursor{c = {_, Owner}}=C,              NumOfAnswers) when Owner =/= self() ->    erlang:error(not_cursor_owner, [C, NumOfAnswers]);next_answers(#qlc_cursor{c = {Pid, _}}=C, NumOfAnswers) ->    N = case NumOfAnswers of            all_remaining -> -1;            _ when is_integer(NumOfAnswers), NumOfAnswers > 0 -> NumOfAnswers;            _ -> erlang:error(badarg, [C, NumOfAnswers])        end,    next_loop(Pid, [], N);next_answers(T1, T2) ->    erlang:error(badarg, [T1, T2]).parse_transform(Forms, Options) ->    qlc_pt:parse_transform(Forms, Options).

⌨️ 快捷键说明

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