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