1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2004-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: %%%---------------------------------------------------------------- 20: %%% Purpose:Test Suite for the 'qlc' module. 21: %%%----------------------------------------------------------------- 22: -module(qlc_SUITE). 23: 24: -define(QLC, qlc). 25: -define(QLCs, "qlc"). 26: 27: %-define(debug, true). 28: 29: %% There are often many tests per testcase. Most tests are copied to a 30: %% module, a file. The file is compiled and the test run. Should the 31: %% test fail, the module file is not removed from ?privdir, but is 32: %% kept for inspection. The name of the file is 33: %% ?privdir/qlc_test_CASE.erl. 34: -define(TESTMODULE, qlc_test). 35: -define(TESTCASE, testcase_name). 36: 37: -ifdef(debug). 38: -define(line, put(line, ?LINE), ). 39: -define(config(X,Y), foo). 40: -define(datadir, ?QLCs ++ "_SUITE_data"). 41: -define(privdir, ?QLCs ++ "_SUITE_priv"). 42: -define(testcase, current_testcase). % don't know 43: -define(t, test_server). 44: -else. 45: -include_lib("test_server/include/test_server.hrl"). 46: -define(datadir, ?config(data_dir, Config)). 47: -define(privdir, ?config(priv_dir, Config)). 48: -define(testcase, ?config(?TESTCASE, Config)). 49: -endif. 50: 51: -include_lib("stdlib/include/ms_transform.hrl"). 52: 53: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 54: init_per_group/2,end_per_group/2, 55: init_per_testcase/2, end_per_testcase/2]). 56: 57: -export([ 58: badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1, 59: filter_var/1, single/1, exported_var/1, generator_vars/1, 60: nomatch/1, errors/1, pattern/1, 61: 62: eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1, 63: evaluator/1, string_to_handle/1, table/1, process_dies/1, 64: sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1, 65: info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1, 66: indices/1, pre_fun/1, skip_filters/1, 67: 68: ets/1, dets/1, 69: 70: join_option/1, join_filter/1, join_lookup/1, join_merge/1, 71: join_sort/1, join_complex/1, 72: 73: otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1, 74: otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1, 75: otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, 76: 77: manpage/1, 78: 79: backward/1, forward/1]). 80: 81: %% Internal exports. 82: -export([bad_table_throw/1, bad_table_exit/1, default_table/1, bad_table/1, 83: bad_table_format/1, bad_table_format_arity/1, bad_table_traverse/1, 84: bad_table_post/1, bad_table_lookup/1, bad_table_max_lookup/1, 85: bad_table_info_arity/1, bad_table_info_fun_n_objects/1, 86: bad_table_info_fun_indices/1, bad_table_info_fun_keypos/1, 87: bad_table_key_equality/1]). 88: -export([evaluator_2/2]). 89: -export([prep_scratchdir/1, truncate_tmpfile/2, crash/2, crash_tmpfile/2]). 90: -export([etsc/2, etsc/3, create_ets/2, lookup_keys/1]). 91: -export([strip_qlc_call/1, join_info/1, join_info_count/1]). 92: -export([i/1, i/2, format_info/2]). 93: 94: -export([table_kill_parent/2, table_parent_throws/2, 95: table_parent_exits/2, table_bad_parent_fun/2]). 96: -export([table/2, table/3, stop_list/2, table_error/2, table_error/3, 97: table_lookup_error/1]). 98: 99: %% error_logger 100: -export([install_error_logger/0, uninstall_error_logger/0, 101: read_error_logger/0]). 102: -export([init/1, 103: handle_event/2, handle_call/2, handle_info/2, 104: terminate/2]). 105: 106: % Default timetrap timeout (set in init_per_testcase). 107: -define(default_timeout, ?t:minutes(5)). 108: 109: init_per_testcase(Case, Config) -> 110: ?line Dog = ?t:timetrap(?default_timeout), 111: [{?TESTCASE, Case}, {watchdog, Dog} | Config]. 112: 113: end_per_testcase(_Case, _Config) -> 114: Dog = ?config(watchdog, _Config), 115: test_server:timetrap_cancel(Dog), 116: ok. 117: 118: suite() -> [{ct_hooks,[ts_install_cth]}]. 119: 120: all() -> 121: [{group, parse_transform}, {group, evaluation}, 122: {group, table_impls}, {group, join}, {group, tickets}, 123: manpage, {group, compat}]. 124: 125: groups() -> 126: [{parse_transform, [], 127: [badarg, nested_qlc, unused_var, lc, fun_clauses, 128: filter_var, single, exported_var, generator_vars, 129: nomatch, errors, pattern]}, 130: {evaluation, [], 131: [eval, cursor, fold, eval_unique, eval_cache, append, 132: evaluator, string_to_handle, table, process_dies, sort, 133: keysort, filesort, cache, cache_list, filter, info, 134: nested_info, lookup1, lookup2, lookup_rec, indices, 135: pre_fun, skip_filters]}, 136: {table_impls, [], [ets, dets]}, 137: {join, [], 138: [join_option, join_filter, join_lookup, join_merge, 139: join_sort, join_complex]}, 140: {tickets, [], 141: [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562, 142: otp_6590, otp_6673, otp_6964, otp_7114, otp_7232, 143: otp_7238, otp_7552, otp_6674, otp_7714]}, 144: {compat, [], [backward, forward]}]. 145: 146: init_per_suite(Config) -> 147: Config. 148: 149: end_per_suite(_Config) -> 150: ok. 151: 152: init_per_group(_GroupName, Config) -> 153: Config. 154: 155: end_per_group(_GroupName, Config) -> 156: Config. 157: 158: badarg(doc) -> 159: "Badarg."; 160: badarg(suite) -> []; 161: badarg(Config) when is_list(Config) -> 162: Ts = 163: [{badarg, 164: <<"-import(qlc, [q/1, q/2]). 165: q(_, _, _) -> ok. 166: 167: badarg() -> 168: qlc:q(foo), 169: qlc:q(foo, cache_all), 170: qlc:q(foo, cache_all, extra), 171: q(bar), 172: q(bar, cache_all), 173: q(bar, cache_all, extra). 174: ">>, 175: [], 176: {errors,[{5,?QLC,not_a_query_list_comprehension}, 177: {6,?QLC,not_a_query_list_comprehension}, 178: {8,?QLC,not_a_query_list_comprehension}, 179: {9,?QLC,not_a_query_list_comprehension}], 180: []}}], 181: ?line [] = compile(Config, Ts), 182: ok. 183: 184: nested_qlc(doc) -> 185: "Nested qlc expressions."; 186: nested_qlc(suite) -> []; 187: nested_qlc(Config) when is_list(Config) -> 188: %% Nested QLC expressions. X is bound before the first one; Z and X 189: %% before the second one. 190: Ts = 191: [{nested_qlc1, 192: <<"nested_qlc() -> 193: X = 3, % X unused 194: Q = qlc:q([Y || 195: X <- % X shadowed 196: begin Z = 3, 197: qlc:q([Y || 198: Y <- [Z], 199: X <- [1,2,3], % X shadowed 200: X < Y]) 201: end, 202: Y <- 203: [y], 204: Y > X]), 205: [y, y] = qlc:e(Q), 206: ok. 207: ">>, 208: [warn_unused_vars], 209: {warnings,[{{2,15},erl_lint,{unused_var,'X'}}, 210: {{4,29},erl_lint,{shadowed_var,'X',generate}}, 211: {{8,49},erl_lint,{shadowed_var,'X',generate}}]}}, 212: 213: {nested_qlc2, 214: <<"nested_qlc() -> 215: H0 = qlc:append([a,b], [c,d]), 216: qlc:q([{X,Y} || 217: X <- H0, 218: Y <- qlc:q([{X,Y} || 219: X <- H0, % X shadowed 220: Y <- H0])]), 221: ok. 222: ">>, 223: [warn_unused_vars], 224: {warnings,[{{6,39},erl_lint,{shadowed_var,'X',generate}}]}} 225: ], 226: ?line [] = compile(Config, Ts), 227: ok. 228: 229: unused_var(doc) -> 230: "Unused variable with a name that should not be introduced."; 231: unused_var(suite) -> []; 232: unused_var(Config) when is_list(Config) -> 233: Ts = 234: [{unused_var, 235: <<"unused_var() -> 236: qlc:q([X || begin Y1 = 3, true end, % Y1 unused 237: Y <- [1,2,3], 238: X <- [a,b,c], 239: X < Y]). 240: ">>, 241: [warn_unused_vars], 242: {warnings,[{{2,33},erl_lint,{unused_var,'Y1'}}]}}], 243: ?line [] = compile(Config, Ts), 244: ok. 245: 246: lc(doc) -> 247: "Ordinary LC expression."; 248: lc(suite) -> []; 249: lc(Config) when is_list(Config) -> 250: Ts = 251: [{lc, 252: <<"lc() -> 253: [X || X <- [], X <- X]. % X shadowed 254: ">>, 255: [], 256: {warnings,[{{2,30},erl_lint,{shadowed_var,'X',generate}}]}}], 257: ?line [] = compile(Config, Ts), 258: ok. 259: 260: fun_clauses(doc) -> 261: "Fun with several clauses."; 262: fun_clauses(suite) -> []; 263: fun_clauses(Config) when is_list(Config) -> 264: Ts = 265: [{fun_clauses, 266: <<"fun_clauses() -> 267: {X,X1,X2} = {1,2,3}, 268: F = fun({X}) -> qlc:q([X || X <- X]); % X shadowed (fun, generate) 269: ([X]) -> qlc:q([X || X <- X]) % X shadowed (fun, generate) 270: end, 271: {F,X,X1,X2}. 272: ">>, 273: [], 274: {warnings,[{{3,22},erl_lint,{shadowed_var,'X','fun'}}, 275: {{3,41},erl_lint,{shadowed_var,'X',generate}}, 276: {{4,22},erl_lint,{shadowed_var,'X','fun'}}, 277: {{4,41},erl_lint,{shadowed_var,'X',generate}}]}}], 278: ?line [] = compile(Config, Ts), 279: ok. 280: 281: filter_var(doc) -> 282: "Variable introduced in filter."; 283: filter_var(suite) -> []; 284: filter_var(Config) when is_list(Config) -> 285: Ts = 286: [{filter_var, 287: <<"filter_var() -> 288: qlc:q([X || 289: Y <- [X || 290: X <- [1,2,3]], 291: begin X = Y, true end]). 292: ">>, 293: [], 294: []}, 295: 296: {unsafe_filter_var, 297: <<"unsafe_filter_var() -> 298: qlc:q([{X,V} || X <- [1,2], 299: case {a} of 300: {_} -> 301: true; 302: V -> 303: V 304: end]). 305: ">>, 306: [], 307: {errors,[{{2,25},erl_lint,{unsafe_var,'V',{'case',{3,19}}}}],[]}}], 308: ?line [] = compile(Config, Ts), 309: ok. 310: 311: 312: single(doc) -> 313: "Unused pattern variable."; 314: single(suite) -> []; 315: single(Config) when is_list(Config) -> 316: Ts = 317: [{single, 318: <<"single() -> 319: qlc:q([X || {X,Y} <- [{1,2}]]), % Y unused 320: qlc:q([[] || [] <- [[]]]). 321: ">>, 322: [warn_unused_vars], 323: {warnings,[{{2,30},erl_lint,{unused_var,'Y'}}]}}], 324: ?line [] = compile(Config, Ts), 325: ok. 326: 327: exported_var(doc) -> 328: "Exported variable in list expression (rhs of generator)."; 329: exported_var(suite) -> []; 330: exported_var(Config) when is_list(Config) -> 331: Ts = 332: [{exported_var, 333: <<"exported() -> 334: qlc:q([X || X <- begin 335: case foo:bar() of 336: 1 -> Z = a; 337: 2 -> Z = b 338: end, 339: [Z = 3, Z = 3] % Z exported (twice...) 340: end 341: ]). 342: ">>, 343: [warn_export_vars], 344: {warnings,[{{7,37},erl_lint,{exported_var,'Z',{'case',{3,36}}}}, 345: {{7,44},erl_lint,{exported_var,'Z',{'case',{3,36}}}}]}}], 346: ?line [] = compile(Config, Ts), 347: ok. 348: 349: generator_vars(doc) -> 350: "Errors for generator variable used in list expression."; 351: generator_vars(suite) -> []; 352: generator_vars(Config) when is_list(Config) -> 353: Ts = 354: [{generator_vars, 355: <<"generator_vars() -> 356: qlc:q([X || 357: Z <- [1,2], 358: X <- begin 359: case 1 of 360: 1 -> Z = a; % used_generator_variable 361: 2 -> Z = b % used_generator_variable 362: end, 363: [Z = 3, Z = 3] % used_generator_variable (2) 364: end 365: ]). 366: ">>, 367: [], 368: {errors,[{{6,41},?QLC,{used_generator_variable,'Z'}}, 369: {{7,41},?QLC,{used_generator_variable,'Z'}}, 370: {{9,33},?QLC,{used_generator_variable,'Z'}}, 371: {{9,40},?QLC,{used_generator_variable,'Z'}}], 372: []}}], 373: ?line [] = compile(Config, Ts), 374: ok. 375: 376: nomatch(doc) -> 377: "Unreachable clauses also found when compiling."; 378: nomatch(suite) -> []; 379: nomatch(Config) when is_list(Config) -> 380: Ts = 381: [{unreachable1, 382: <<"unreachable1() -> 383: qlc:q([X || X <- [1,2], 384: case X of 385: true -> false; 386: true -> true % clause cannot match 387: end]). 388: ">>, 389: [], 390: {warnings,[{5,v3_kernel,{nomatch_shadow,4}}]}}, 391: 392: {nomatch1, 393: <<"generator1() -> 394: qlc:q([3 || {3=4} <- []]). 395: ">>, 396: [], 397: {warnings,[{{2,27},qlc,nomatch_pattern}]}}, 398: 399: {nomatch2, 400: <<"nomatch() -> 401: etsc(fun(E) -> 402: Q = qlc:q([3 || {3=4} <- ets:table(E)]), 403: [] = qlc:eval(Q), 404: false = lookup_keys(Q) 405: end, [{1},{2}]). 406: ">>, 407: [], 408: {warnings,[{{3,33},qlc,nomatch_pattern}]}}, 409: 410: {nomatch3, 411: <<"nomatch() -> 412: etsc(fun(E) -> 413: Q = qlc:q([{A,B,C,D} || A=B={C=D}={_,_} <- 414: ets:table(E)]), 415: [] = qlc:eval(Q), 416: false = lookup_keys(Q) 417: end, [{1,2},{2,3}]). 418: ">>, 419: [], 420: {warnings,[{{3,52},qlc,nomatch_pattern}]}}, 421: 422: {nomatch4, 423: <<"nomatch() -> 424: etsc(fun(E) -> 425: Q = qlc:q([{X,Y} || {<<X>>} = {<<Y>>} <- 426: ets:table(E)]), 427: [] = qlc:eval(Q), 428: false = lookup_keys(Q) 429: end, [{<<34>>},{<<40>>}]). 430: ">>, 431: [], 432: {errors,[{{3,48},erl_lint,illegal_bin_pattern}],[]}}, 433: 434: {nomatch5, 435: <<"nomatch() -> 436: etsc(fun(E) -> 437: Q = qlc:q([t || {\"a\"++\"b\"} = {\"ac\"} <- 438: ets:table(E)]), 439: [t] = qlc:eval(Q), 440: [\"ab\"] = lookup_keys(Q) 441: end, [{\"ab\"}]). 442: ">>, 443: [], 444: {warnings,[{3,v3_core,nomatch}]}} 445: 446: ], 447: ?line [] = compile(Config, Ts), 448: ok. 449: 450: 451: errors(doc) -> 452: "Errors within qlc expressions also found when compiling."; 453: errors(suite) -> []; 454: errors(Config) when is_list(Config) -> 455: Ts = 456: [{errors1, 457: <<"errors1() -> 458: qlc:q([X || X <- A]). % A unbound 459: ">>, 460: [], 461: {errors,[{{2,33},erl_lint,{unbound_var,'A'}}],[]}}], 462: ?line [] = compile(Config, Ts), 463: ok. 464: 465: pattern(doc) -> 466: "Patterns."; 467: pattern(suite) -> []; 468: pattern(Config) when is_list(Config) -> 469: Ts = [ 470: <<"%% Records in patterns. No lookup. 471: L = [#a{k=#k{v=91}}], 472: H = qlc:q([Q || Q = #a{k=#k{v=91}} <- qlc_SUITE:table(L, 2, [])]), 473: {qlc,_,[{generate,_,{table,{call,_,_,_}}}], []} = i(H), 474: L = qlc:e(H), 475: {call, _, _q, [{lc,_,{var,_,'Q'}, 476: [{generate,_, 477: {match,_,_,_}, 478: {call,_,_,_}}]}]} 479: = i(H, {format,abstract_code})">>, 480: 481: <<"%% No matchspec since there is a binary in the pattern. 482: etsc(fun(E) -> 483: Q = qlc:q([A || {<<A:3/unit:8>>} <- ets:table(E)]), 484: [_] = qlc:eval(Q), 485: {qlc,_,[{generate,_,{table,_}}], []} = i(Q) 486: end, [{<<\"hej\">>}])">> 487: 488: ], 489: ?line run(Config, <<"-record(a, {k,v}). 490: -record(k, {t,v}).\n">>, Ts), 491: ok. 492: 493: 494: eval(doc) -> 495: "eval/2"; 496: eval(suite) -> []; 497: eval(Config) when is_list(Config) -> 498: ScratchDir = filename:join([?privdir, "scratch","."]), 499: 500: Ts = [<<"{'EXIT',{badarg,_}} = (catch qlc:eval(not_a_qlc)), 501: H = qlc:q([X || X <- [1,2]]), 502: {'EXIT',{{unsupported_qlc_handle,{qlc_handle,foo}},_}}= 503: (catch qlc:e({qlc_handle,foo})), 504: {'EXIT',{badarg,_}} = (catch qlc:eval(H, [{unique_all,badarg}])), 505: {'EXIT',{badarg,_}} = 506: (catch qlc:eval(H, [{spawn_options,badarg}])), 507: {'EXIT',{badarg,_}} = 508: (catch qlc:eval(H, [{unique_all,true},{bad,arg}])), 509: {throw,t} = 510: (catch {any_term,qlc:e(qlc:q([X || X <- throw({throw,t})]))}), 511: M = qlc, 512: {'EXIT',{badarg,_}} = (catch M:q(bad))">>, 513: 514: [<<"Dir = \"">>,ScratchDir,<<"\", 515: qlc_SUITE:prep_scratchdir(Dir), 516: 517: E = ets:new(foo, []), 518: [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})], 519: H = qlc:q([{X,Y} || Y <- [1,2], 520: X <- qlc:sort(ets:table(E),{tmpdir,Dir}), 521: qlc_SUITE:truncate_tmpfile(Dir, 0)]), 522: R = qlc:eval(H), 523: ets:delete(E), 524: {error,_,{bad_object,_}} = R, 525: \"the tempo\" ++ _ = lists:flatten(qlc:format_error(R))">>], 526: 527: [<<"Dir = \"">>,ScratchDir,<<"\", 528: qlc_SUITE:prep_scratchdir(Dir), 529: 530: E = ets:new(foo, []), 531: Bin = term_to_binary(lists:seq(1,20000)), 532: [true || I <- lists:seq(1, 10), not ets:insert(E, {I, I, Bin})], 533: H = qlc:q([{X,Y} || Y <- [1,2], 534: X <- qlc:sort(ets:table(E),{tmpdir,Dir}), 535: qlc_SUITE:crash_tmpfile(Dir, 5)]), 536: R = qlc:eval(H), 537: ets:delete(E), 538: {error,_,{bad_object,_}} = R">>], 539: 540: <<"E = ets:new(test, []), 541: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 542: Y <- ets:table(E)]), 543: R1 = (catch {any_term,qlc:eval(H, {unique_all,false})}), 544: R2 = (catch {any_term,qlc:eval(H, {unique_all,true})}), 545: ets:delete(E), 546: true = {throw,bad_pre_fun} == R1, 547: true = {throw,bad_pre_fun} == R2">>, 548: 549: <<"E = ets:new(test, []), 550: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 551: Y <- ets:table(E)]), 552: R1 = (catch qlc:eval(H, {unique_all,false})), 553: R2 = (catch qlc:eval(H, {unique_all,true})), 554: ets:delete(E), 555: {'EXIT',{bad_pre_fun,_}} = R1, 556: {'EXIT',{bad_pre_fun,_}} = R2">>, 557: 558: <<"Q = qlc:q([X || X <- [4,3,2,1,0,-1], begin 3/X > 0 end]), 559: {'EXIT',{badarith,_}} = (catch qlc:eval(Q, {unique_all,false})), 560: {'EXIT',{badarith,_}} = (catch qlc:eval(Q, {unique_all,true})) 561: ">>, 562: 563: <<"[1,2] = qlc:eval(qlc:q([X || X <- [1,2]])), 564: [1,2,3,4] = qlc:eval(qlc:append([1,2],[3,4])), 565: [1,2] = qlc:eval(qlc:sort([2,1])), 566: E = ets:new(foo, []), 567: ets:insert(E, [{1},{2}]), 568: [{1},{2}] = lists:sort(qlc:eval(ets:table(E))), 569: true = ets:delete(E)">>, 570: 571: <<"H = qlc:q([X || X <- [1,2], 572: begin F = fun() -> 573: qlc:e(qlc:q([Y || Y <- [1,2]])) end, 574: F() == (fun f/0)() end]), 575: [1,2] = qlc:e(H), 576: ok. 577: 578: f() -> [1,2]. 579: foo() -> bar">>, 580: 581: <<"C1_0_1 = [1,2], 582: %% The PT cannot rename C to C1_0_1; another name is chosen. 583: [1,2] = qlc:eval(qlc:q([C || C <- C1_0_1]))">>, 584: 585: <<"H = qlc:q([X || {X,X} <- [{1,a},{2,2},{b,b},{3,4}]]), 586: [2,b] = qlc:e(H), 587: H1 = qlc:q([3 || {X,X} <- [{1,a},{2,2},{b,b},{3,4}]]), 588: [3,3] = qlc:e(H1)">>, 589: 590: %% Just to cover a certain line in qlc.erl (avoids returning []) 591: <<"E = ets:new(foo, []), 592: Bin = term_to_binary(lists:seq(1,20000)), 593: [true || I <- lists:seq(1, 10), not ets:insert(E, {I, I, Bin})], 594: H = qlc:q([{X,Y} || Y <- [1,2], X <- qlc:sort(ets:table(E))]), 595: R = qlc:eval(H), 596: ets:delete(E), 597: 20 = length(R)">>, 598: 599: <<"H = qlc:q([{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} || 600: {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} <- 601: [{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w}]]), 602: [_] = qlc:e(H)">>, 603: 604: <<"H = qlc:q([Y || Y <- [1,2]], 605: {unique, begin [T] = qlc:e(qlc:q([X || X <- [true]], 606: cache)), 607: T end}), 608: [1,2] = qlc:e(H)">> 609: 610: ], 611: 612: ?line run(Config, Ts), 613: ok. 614: 615: cursor(doc) -> 616: "cursor/2"; 617: cursor(suite) -> []; 618: cursor(Config) when is_list(Config) -> 619: ScratchDir = filename:join([?privdir, "scratch","."]), 620: Ts = [<<"{'EXIT',{badarg,_}} = 621: (catch qlc:cursor(fun() -> not_a_cursor end)), 622: H0 = qlc:q([X || X <- throw({throw,t})]), 623: {throw,t} = (catch {any_term,qlc:cursor(H0)}), 624: H = qlc:q([X || X <- [1,2]]), 625: {'EXIT',{badarg,_}} = 626: (catch qlc:cursor(H,{spawn_options, [a|b]})), 627: {'EXIT',{badarg,_}} = 628: (catch qlc:cursor(H,{bad_option,true}))">>, 629: 630: <<"{'EXIT',{badarg,_}} = (catch qlc:delete_cursor(not_a_cursor))">>, 631: 632: [<<"Dir = \"">>,ScratchDir,<<"\", 633: qlc_SUITE:prep_scratchdir(Dir), % kludge 634: E = ets:new(foo, []), 635: [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})], 636: H = qlc:q([{X,Y} || begin put('$qlc_tmpdir', true), true end, 637: Y <- [1,2], 638: X <- qlc:sort(ets:table(E),{tmpdir,Dir}), 639: qlc_SUITE:truncate_tmpfile(Dir, 0)]), 640: C = qlc:cursor(H), 641: R = qlc:next_answers(C, all_remaining), 642: qlc:delete_cursor(C), 643: erase('$qlc_tmpdir'), 644: ets:delete(E), 645: {error,_,{bad_object,_}} = R">>], 646: 647: <<"H1 = qlc:q([X || X <- [1,2]]), 648: C1 = qlc:cursor(H1), 649: [1,2] = qlc:next_answers(C1, all_remaining), 650: [] = qlc:next_answers(C1), 651: [] = qlc:next_answers(C1), 652: ok = qlc:delete_cursor(C1), 653: 654: H2 = qlc:append([1,2],[3,4]), 655: C2 = qlc:cursor(H2), 656: [1,2,3,4] = qlc:next_answers(C2, all_remaining), 657: ok = qlc:delete_cursor(C2), 658: 659: H3 = qlc:sort([2,1]), 660: C3 = qlc:cursor(H3), 661: [1,2] = qlc:next_answers(C3, all_remaining), 662: ok = qlc:delete_cursor(C3), 663: 664: E = ets:new(foo, []), 665: ets:insert(E, [{1},{2}]), 666: H4 = ets:table(E), 667: C4 = qlc:cursor(H4), 668: [{1},{2}] = lists:sort(qlc:next_answers(C4, all_remaining)), 669: ok = qlc:delete_cursor(C4), 670: true = ets:delete(E)">>, 671: 672: <<"H = qlc:q([{X,Y} || X <- [1,2], Y <- [a,b]]), 673: C = qlc:cursor(H, []), 674: [{1,a},{1,b}] = qlc:next_answers(C, 2), 675: [{2,a}] = qlc:next_answers(C, 1), 676: [{2,b}] = qlc:next_answers(C, all_remaining), 677: {'EXIT',{badarg,_}} = (catch qlc:next_answers(C, -1)), 678: P = self(), 679: Pid1 = spawn_link(fun() -> 680: {'EXIT',{not_cursor_owner,_}} = 681: (catch qlc:delete_cursor(C)), 682: P ! {self(), done} end), 683: Pid2 = spawn_link(fun() -> 684: {'EXIT',{not_cursor_owner,_}} = 685: (catch qlc:next_answers(C)), 686: P ! {self(), done} end), 687: receive {Pid1, done} -> ok end, 688: receive {Pid2, done} -> ok end, 689: ok = qlc:delete_cursor(C), 690: {'EXIT',{badarg,_}} = (catch qlc:next_answers(not_a_cursor)), 691: ok = qlc:delete_cursor(C)">>, 692: 693: <<"Q = qlc:q([X || X <- [1,2,1,2,1]]), 694: C1 = qlc:cursor(Q, [{unique_all,true}]), 695: [1,2] = qlc:next_answers(C1, all_remaining), 696: ok = qlc:delete_cursor(C1), 697: C2 = qlc:cursor(Q, [{unique_all,true}]), 698: [1,2] = qlc:next_answers(C2, all_remaining), 699: ok = qlc:delete_cursor(C2)">>, 700: 701: <<"Q = qlc:q([X || X <- [1,2,1,2,1]]), 702: C1 = qlc:cursor(Q, [{unique_all,true},{spawn_options, []}]), 703: [1,2] = qlc:next_answers(C1, all_remaining), 704: ok = qlc:delete_cursor(C1), 705: C2 = qlc:cursor(Q, [{unique_all,true},{spawn_options, default}]), 706: [1,2] = qlc:next_answers(C2, all_remaining), 707: ok = qlc:delete_cursor(C2)">>, 708: 709: <<"Q = qlc:q([X || X <- [1,2,1,2,1]]), 710: C1 = qlc:cursor(Q, [{unique_all,false},{spawn_options, []}]), 711: [1,2,1,2,1] = qlc:next_answers(C1, all_remaining), 712: ok = qlc:delete_cursor(C1), 713: C2 = qlc:cursor(Q, [{unique_all,false},{spawn_options, []}]), 714: [1,2,1,2,1] = qlc:next_answers(C2, all_remaining), 715: ok = qlc:delete_cursor(C2)">>, 716: 717: <<"Q = qlc:q([X || X <- [1,2,1,2,1]]), 718: C1 = qlc:cursor(Q, [{unique_all,false}]), 719: [1,2,1,2,1] = qlc:next_answers(C1, all_remaining), 720: ok = qlc:delete_cursor(C1), 721: C2 = qlc:cursor(Q, [{unique_all,false}]), 722: [1,2,1,2,1] = qlc:next_answers(C2, all_remaining), 723: ok = qlc:delete_cursor(C2)">> 724: 725: ], 726: ?line run(Config, Ts), 727: ok. 728: 729: fold(doc) -> 730: "fold/4"; 731: fold(suite) -> []; 732: fold(Config) when is_list(Config) -> 733: ScratchDir = filename:join([?privdir, "scratch","."]), 734: Ts = [<<"Q = qlc:q([X || X <- [1,2,1,2,1]]), 735: F = fun(Obj, A) -> A++[Obj] end, 736: {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], Q, {bad,arg})), 737: {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], badarg)), 738: {'EXIT',{badarg,_}} = 739: (catch qlc:fold(F, [], {spawn_options, [a|b]})), 740: H = qlc:q([X || X <- throw({throw,t})]), 741: {throw,t} = (catch {any_term,qlc:fold(F, [], H)}), 742: [1,2] = qlc:fold(F, [], Q, {unique_all,true}), 743: {'EXIT',{badarg,_}} = 744: (catch qlc:fold(F, [], Q, [{unique_all,bad}])), 745: [1,2,1,2,1] = 746: qlc:fold(F, [], Q, [{unique_all,false}])">>, 747: 748: [<<"Dir = \"">>,ScratchDir,<<"\", 749: qlc_SUITE:prep_scratchdir(Dir), 750: 751: E = ets:new(foo, []), 752: [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})], 753: H = qlc:q([{X,Y} || Y <- [1,2], 754: X <- qlc:sort(ets:table(E),{tmpdir,Dir}), 755: qlc_SUITE:truncate_tmpfile(Dir, 0)]), 756: F = fun(Obj, A) -> A++[Obj] end, 757: R = qlc:fold(F, [], H), 758: ets:delete(E), 759: {error,_,{bad_object,_}} = R">>], 760: 761: <<"E = ets:new(test, []), 762: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 763: Y <- ets:table(E)]), 764: F = fun(Obj, A) -> A++[Obj] end, 765: R1 = (catch {any_term,qlc:fold(F, [], H, {unique_all,false})}), 766: R2 = (catch {any_term,qlc:fold(F, [], H, {unique_all,true})}), 767: ets:delete(E), 768: true = {throw,bad_pre_fun} == R1, 769: true = {throw,bad_pre_fun} == R2">>, 770: 771: <<"E = ets:new(test, []), 772: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 773: Y <- ets:table(E)]), 774: F = fun(Obj, A) -> A++[Obj] end, 775: R1 = (catch qlc:fold(F, [], H, {unique_all,false})), 776: R2 = (catch qlc:fold(F, [], H, {unique_all,true})), 777: ets:delete(E), 778: {'EXIT',{bad_pre_fun,_}} = R1, 779: {'EXIT',{bad_pre_fun,_}} = R2">>, 780: 781: <<"F = fun(Obj, A) -> A++[Obj] end, 782: Q = qlc:q([X || X <- [1,2,1,2,1], throw({throw,wrong})]), 783: {throw,wrong} = 784: (catch {any_term,qlc:fold(F, [], Q, {unique_all,true})}), 785: {throw,wrong} = 786: (catch {any_term,qlc:fold(F, [], Q)})">>, 787: 788: <<"F = fun(Obj, A) -> A++[Obj] end, 789: Q = qlc:q([X || X <- [4,3,2,1,0,-1], begin 3/X > 0 end]), 790: {'EXIT',{badarith,_}} = 791: (catch qlc:fold(F, [], Q, {unique_all,true})), 792: {'EXIT',{badarith,_}} = 793: (catch qlc:fold(F, [], Q, [{unique_all,false}])) 794: ">>, 795: 796: <<"F = fun(Obj, A) -> A++[Obj] end, 797: [1,2] = qlc:fold(F, [], qlc:q([X || X <- [1,2]])), 798: [1,2,3,4] = qlc:fold(F, [], qlc:append([1,2],[3,4])), 799: [1,2] = qlc:fold(F, [], qlc:sort([2,1])), 800: E = ets:new(foo, []), 801: ets:insert(E, [{1},{2}]), 802: [{1},{2}] = lists:sort(qlc:fold(F, [], ets:table(E))), 803: true = ets:delete(E)">>, 804: 805: <<"F = fun(_Obj, _A) -> throw({throw,fatal}) end, 806: Q = qlc:q([X || X <- [4,3,2]]), 807: {throw,fatal} = 808: (catch {any_term,qlc:fold(F, [], Q, {unique_all,true})}), 809: {throw,fatal} = 810: (catch {any_term,qlc:fold(F, [], Q, [{unique_all,false}])})">>, 811: 812: <<"G = fun(_Obj, _A, D) -> 17/D end, 813: F = fun(Obj, A) -> G(Obj, A, 0) end, 814: Q = qlc:q([X || X <- [4,3,2]]), 815: {'EXIT',{badarith,_}} = 816: (catch qlc:fold(F, [], Q, {unique_all,true})), 817: {'EXIT',{badarith,_}} = 818: (catch qlc:fold(F, [], Q, [{unique_all,false}])) 819: ">> 820: ], 821: ?line run(Config, Ts), 822: ok. 823: 824: eval_unique(doc) -> 825: "Test the unique_all option of eval."; 826: eval_unique(suite) -> []; 827: eval_unique(Config) when is_list(Config) -> 828: Ts = [<<"QLC1 = qlc:q([X || X <- qlc:append([[1,1,2], [1,2,3,2,3]])]), 829: [1,2,3] = qlc:eval(QLC1, {unique_all,true}), 830: QLC2 = qlc:q([X || X <- [1,2,1,2,1,2,1]]), 831: [1,2] = qlc:e(QLC2, {unique_all,true})">>, 832: 833: <<"E = ets:new(test, []), 834: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 835: H = qlc:q([X || X <- qlc:append([ets:table(E), ets:table(E)])]), 836: R1 = qlc:e(H, {unique_all,false}), 837: R2 = qlc:e(H, {unique_all,true}), 838: ets:delete(E), 839: true = lists:sort(R1) == [{1,a},{1,a},{2,b},{2,b},{3,c},{3,c}], 840: true = lists:sort(R2) == [{1,a},{2,b},{3,c}] 841: ">>, 842: 843: <<"Q1 = qlc:q([{X,make_ref()} || X <- [1,2,1,2]]), 844: [_,_] = qlc:e(Q1, {unique_all,true}), 845: [_,_,_,_] = qlc:e(Q1, {unique_all,false}), 846: [_,_] = qlc:e(Q1, [{unique_all,true}]), 847: Q2 = qlc:q([{X,make_ref()} || X <- qlc:append([[1,2,1,2]])]), 848: [_,_] = qlc:e(Q2, {unique_all,true}), 849: [_,_,_,_] = qlc:e(Q2, {unique_all,false}), 850: [_,_] = qlc:e(Q2, [{unique_all,true}]) 851: ">>, 852: 853: <<"Q = qlc:q([{X,make_ref()} || X <- qlc:sort([1,2,1,2])]), 854: [_, _] = qlc:e(Q, {unique_all,true}), 855: Q1 = qlc:q([X || X <- [1,2,1,2]]), 856: Q2 = qlc:q([{X,make_ref()} || X <- qlc:sort(Q1)]), 857: [_, _] = qlc:e(Q2, {unique_all,true}) 858: ">>, 859: 860: <<"E = ets:new(test, []), 861: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 862: H = qlc:q([X || X <- qlc:append([[1,2,1,2]])]), 863: [1,2,1,2] = qlc:e(H, {unique_all,false}), 864: [1,2] = qlc:e(H, {unique_all,true}), 865: ets:delete(E)">>, 866: 867: <<"E = ets:new(foo, [duplicate_bag]), 868: true = ets:insert(E, [{1,a},{1,a},{2,b},{3,c},{4,c},{4,d}]), 869: Q1 = qlc:q([{X,make_ref()} || {_, X} <- ets:table(E)]), 870: true = length(qlc:eval(Q1, {unique_all, true})) =:= 5, 871: Q2 = qlc:q([X || {_, X} <- ets:table(E)]), 872: true = length(qlc:eval(Q2, {unique_all, true})) =:= 4, 873: Q3 = qlc:q([element(2, X) || X <- ets:table(E)]), 874: true = length(qlc:eval(Q3, {unique_all, true})) =:= 4, 875: Q4 = qlc:q([1 || _X <- ets:table(E)]), 876: true = length(qlc:eval(Q4, {unique_all, true})) =:= 1, 877: true = ets:delete(E) 878: ">>, 879: 880: <<"Q1 = qlc:q([X || X <- qlc:append([[1], [2,1]])]), 881: Q2 = qlc:q([X || X <- qlc:append([[2,1], [2]])]), 882: Q3 = qlc:q([{X,Y} || X <- Q1, Y <- Q2]), 883: [{1,2},{1,1},{2,2},{2,1}] = qlc:e(Q3, {unique_all,true}), 884: Q4 = qlc:q([{X,Y,make_ref()} || X <- Q1, Y <- Q2]), 885: [{1,2,_},{1,1,_},{2,2,_},{2,1,_}] = qlc:e(Q4, {unique_all,true}) 886: ">>, 887: 888: <<"Q1 = qlc:q([X || X <- [1,2,1]]), 889: Q2 = qlc:q([X || X <- [2,1,2]]), 890: Q3 = qlc:q([{X,Y} || X <- Q1, Y <- Q2]), 891: [{1,2},{1,1},{2,2},{2,1}] = qlc:e(Q3,{unique_all,true}), 892: Q4 = qlc:q([{X,Y,make_ref()} || X <- Q1, Y <- Q2]), 893: [{1,2,_},{1,1,_},{2,2,_},{2,1,_}] = qlc:e(Q4, {unique_all,true}) 894: ">>, 895: 896: <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{1,b}]]), 897: [1] = qlc:e(Q1, {unique_all, true}), 898: Q2 = qlc:q([a || _ <- [{1,a},{1,b}]]), 899: [a] = qlc:e(Q2, {unique_all, true}) 900: ">>, 901: 902: <<"Q = qlc:q([SQV || SQV <- qlc:q([X || X <- [1,2,1]],unique)], 903: unique), 904: {call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} = 905: qlc:info(Q, [{format,abstract_code},unique_all]), 906: [1,2] = qlc:e(Q)">>, 907: 908: <<"Q = qlc:q([X || X <- [1,2,1]]), 909: {call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} = 910: qlc:info(Q, [{format,abstract_code},unique_all]), 911: [1,2] = qlc:e(Q, unique_all)">>, 912: 913: <<"Q1 = qlc:sort([{1},{2},{3},{1}], [{unique,true}]), 914: Q = qlc:sort(Q1,[{unique,true}]), 915: {sort,{sort,{list,_},[{unique,true}]},[]} = i(Q)">> 916: 917: ], 918: ?line run(Config, Ts), 919: ok. 920: 921: eval_cache(doc) -> 922: "Test the cache_all and unique_all options of eval."; 923: eval_cache(suite) -> []; 924: eval_cache(Config) when is_list(Config) -> 925: Ts = [ 926: <<"E = ets:new(apa, [ordered_set]), 927: ets:insert(E, [{1},{2}]), 928: H = qlc:q([X || Y <- [3,4], 929: ets:insert(E, {Y}), 930: X <- ets:table(E)]), % already unique, no cache... 931: {qlc, _, 932: [{generate, _, {qlc, _, 933: [{generate, _, {list, [3,4]}}], 934: [{unique,true}]}}, 935: _, 936: {generate, _, {table,_}}], 937: [{unique,true}]} = i(H, [cache_all, unique_all]), 938: [{1},{2},{3},{4}] = qlc:e(H, [cache_all, unique_all]), 939: ets:delete(E)">>, 940: 941: <<"E = ets:new(apa, [ordered_set]), 942: ets:insert(E, [{1},{2}]), 943: H = qlc:q([X || Y <- [3,4], 944: ets:insert(E, {Y}), 945: X <- ets:table(E)]), % no cache... 946: {qlc, _, 947: [{generate, _,{list, [3,4]}}, 948: _, 949: {generate, _, {table,_}}], 950: []} = i(H, cache_all), 951: [{1},{2},{3},{1},{2},{3},{4}] = qlc:e(H, [cache_all]), 952: ets:delete(E)">>, 953: 954: <<"E = ets:new(apa, [ordered_set]), 955: ets:insert(E, [{1},{2}]), 956: H = qlc:q([X || Y <- [3,4], 957: ets:insert(E, {Y}), 958: X <- qlc:q([X || X <- ets:table(E)], cache)]), 959: {qlc, _, 960: [{generate, _, {list, [3,4]}}, 961: _, 962: {generate, _, {qlc, _, 963: [{generate, _, {table,_}}], 964: [{cache,ets}]}}], 965: []} = i(H, cache_all), 966: [{1},{2},{3},{1},{2},{3}] = qlc:e(H, [cache_all]), 967: ets:delete(E)">>, 968: 969: <<"%% {cache_all,no} does not override {cache,true}. 970: E = ets:new(apa, [ordered_set]), 971: ets:insert(E, [{1},{2}]), 972: H = qlc:q([X || Y <- [3,4], 973: ets:insert(E, {Y}), 974: X <- qlc:q([X || X <- ets:table(E)], cache)]), 975: {qlc, _, 976: [{generate, _, {list, [3,4]}}, 977: _, 978: {generate, _, {qlc, _, 979: [{generate, _, {table,_}}], 980: [{cache,ets}]}}], 981: []} = i(H, {cache_all,no}), 982: [{1},{2},{3},{1},{2},{3}] = qlc:e(H, [cache_all]), 983: ets:delete(E)">>, 984: 985: <<"E = ets:new(apa, [ordered_set]), 986: ets:insert(E, [{1},{2}]), 987: H = qlc:q([X || Y <- [3,4], 988: ets:insert(E, {Y}), 989: X <- ets:table(E)]), 990: {qlc, _, 991: [{generate, _, {qlc, _, [{generate, _,{list, [3,4]}}], 992: [{unique,true}]}}, 993: _, 994: {generate, _,{table,_}}], 995: [{unique,true}]} = i(H, unique_all), 996: [{1},{2},{3},{4}] = qlc:e(H, [unique_all]), 997: ets:delete(E)">>, 998: 999: %% cache_all is ignored 1000: <<"E = ets:new(apa, [ordered_set]), 1001: ets:insert(E, [{1},{2},{0}]), 1002: H = qlc:q([X || X <- qlc:sort(ets:table(E))]), 1003: {sort,_Table,[]} = i(H, cache_all), 1004: [{0},{1},{2}] = qlc:e(H, cache_all), 1005: ets:delete(E)">>, 1006: 1007: <<"F = fun(Obj, A) -> A++[Obj] end, 1008: E = ets:new(apa, [duplicate_bag]), 1009: true = ets:insert(E, [{1,a},{2,b},{1,a}]), 1010: Q = qlc:q([X || X <- ets:table(E)], cache), 1011: {table, _} = i(Q, []), 1012: R = qlc:fold(F, [], Q, []), 1013: ets:delete(E), 1014: true = [{1,a},{1,a},{2,b}] == lists:sort(R)">>, 1015: 1016: <<"E = ets:new(apa, [ordered_set]), 1017: ets:insert(E, [{1},{2},{0}]), 1018: H = qlc:q([X || X <- ets:table(E)], cache), 1019: {table, _} = i(H, cache_all), 1020: [{0},{1},{2}]= qlc:e(H, cache_all), 1021: ets:delete(E)">>, 1022: 1023: <<"E = ets:new(foo, []), 1024: true = ets:insert(E, [{1}, {2}]), 1025: Q1 = qlc:q([{X} || X <- ets:table(E)]), 1026: Q2 = qlc:q([{X,Y} || {X} <- Q1, {Y} <- Q1]), 1027: {qlc, _, [{generate, _, {table, _}}, 1028: {generate, _, {qlc, _, [{generate, _, {table, _}}], 1029: [{cache,ets}]}}], 1030: []} = i(Q2, cache_all), 1031: [{{1},{1}},{{1},{2}},{{2},{1}},{{2},{2}}] = 1032: lists:sort(qlc:e(Q2, cache_all)), 1033: ets:delete(E)">>, 1034: 1035: <<"L1 = [1,2,3], 1036: L2 = [4,5,6], 1037: Q1 = qlc:append(L1, L2), 1038: Q2 = qlc:q([{X} || X <- Q1]), 1039: {qlc, _,[{generate, _,{append, [{list, L1}, {list, L2}]}}], []} = 1040: i(Q2, [cache_all]), 1041: [{1},{2},{3},{4},{5},{6}] = qlc:e(Q2, [cache_all])">>, 1042: 1043: <<"H = qlc:sort(qlc:q([1 || _ <- [a,b]])), 1044: {sort, {qlc, _, [{generate, _, {qlc, _, [{generate, _, 1045: {list, [a,b]}}], 1046: [{unique,true}]}}], 1047: [{unique,true}]}, 1048: []} = i(H, unique_all), 1049: [1] = qlc:e(H, unique_all)">> 1050: 1051: ], 1052: ?line run(Config, Ts), 1053: ok. 1054: 1055: append(doc) -> 1056: "Test the append function."; 1057: append(suite) -> []; 1058: append(Config) when is_list(Config) -> 1059: Ts = [<<"C = qlc:cursor(qlc:q([X || X <- [0,1,2,3], begin 10/X > 0.0 end])), 1060: R = (catch qlc:next_answers(C)), 1061: {'EXIT',{badarith,_}} = R">>, 1062: 1063: <<"C = qlc:cursor(qlc:q([X || X <- [0 | fun() -> exit(bad) end]])), 1064: R = (catch qlc:next_answers(C)), 1065: {'EXIT',bad} = R">>, 1066: 1067: <<"{'EXIT',{badarg,_}} = (catch qlc:append([a], a)), 1068: {'EXIT',{badarg,_}} = (catch qlc:append([[a],a]))">>, 1069: 1070: <<"C = qlc:cursor(qlc:q([X || X <- [0,1,2,3], 1071: begin throw({throw,wrong}), true end])), 1072: {throw,wrong} = (catch {any_term,qlc:next_answers(C)})">>, 1073: 1074: <<"QLC = qlc:q([X || X <- [0,1,2,3], 1075: begin throw({throw,wrong}), true end]), 1076: {throw,wrong} = (catch {any_term,qlc:eval(QLC)}), 1077: {throw,wrong} = 1078: (catch {any_term,qlc:e(QLC, {unique_all,true})})">>, 1079: 1080: <<"H1 = qlc:q([X || X <- [1,2,3]]), 1081: H2 = qlc:q([X || X <- [4,5,6]]), 1082: R = qlc:e(qlc:q([X || X <- qlc:append([H1, H2])])), 1083: true = R == [1,2,3,4,5,6]">>, 1084: 1085: <<"H1 = [1,2,3], 1086: H2 = qlc:q([X || X <- [4,5,6]]), 1087: R = qlc:e(qlc:q([X || X <- qlc:append(H1, H2)])), 1088: true = R == [1,2,3,4,5,6]">>, 1089: 1090: <<"H1 = qlc:q([X || X <- [1,2,3]]), 1091: H2 = qlc:q([X || X <- [4,5,6]]), 1092: R = qlc:e(qlc:q([X || X <- qlc:append(qlc:e(H1), H2)])), 1093: true = R == [1,2,3,4,5,6]">>, 1094: 1095: <<"H1 = qlc:q([X || X <- [1,2,3]]), 1096: H2 = [4,5,6], 1097: R = qlc:e(qlc:q([X || X <- qlc:append(H1, H2)])), 1098: true = R == [1,2,3,4,5,6]">>, 1099: 1100: <<"H1 = qlc:q([X || X <- [1,2,3]]), 1101: H2 = qlc:q([X || X <- [4,5,6]]), 1102: R = qlc:e(qlc:q([X || X <- qlc:append([H1, H2, H1]), X < 5])), 1103: true = R == [1,2,3,4,1,2,3]">>, 1104: 1105: <<"R = qlc:e(qlc:q([X || X <- qlc:append([lista(), anrop()])])), 1106: true = R == [a,b,1,2], 1107: ok. 1108: 1109: lista() -> 1110: [a,b]. 1111: 1112: anrop() -> 1113: qlc:q([X || X <- [1,2]]). 1114: foo() -> bar">>, 1115: 1116: %% Used to work up to R11B. 1117: % <<"apa = qlc:e(qlc:q([X || X <- qlc:append([[1,2,3], ugly()])])), 1118: % ok. 1119: % 1120: % ugly() -> 1121: % [a | apa]. 1122: % foo() -> bar">>, 1123: 1124: 1125: %% Maybe this one should fail. 1126: <<"[a|b] = qlc:e(qlc:q([X || X <- qlc:append([[a|b]])])), 1127: ok">>, 1128: 1129: <<"17 = qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],ugly2()])])), 1130: ok. 1131: 1132: ugly2() -> 1133: [a | fun() -> 17 end]. 1134: foo() -> bar">>, 1135: 1136: <<"E = ets:new(test, []), 1137: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1138: H = qlc:q([X || X <- qlc:append([ets:table(E), apa])]), 1139: {'EXIT',{badarg,_}} = (catch qlc:e(H)), 1140: false = ets:info(E, safe_fixed), 1141: {'EXIT',{badarg,_}} = (catch qlc:e(H)), 1142: false = ets:info(E, safe_fixed), 1143: {'EXIT',{badarg,_}} = (catch qlc:cursor(H)), 1144: false = ets:info(E, safe_fixed), 1145: F = fun(Obj, A) -> A++[Obj] end, 1146: {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], H)), 1147: false = ets:info(E, safe_fixed), 1148: ets:delete(E)">>, 1149: 1150: <<"H1 = qlc:q([X || X <- [1,2,3]]), 1151: H2 = qlc:q([X || X <- [a,b,c]]), 1152: R = qlc:e(qlc:q([X || X <- qlc:append(H1,qlc:append(H1,H2))])), 1153: true = R == [1,2,3,1,2,3,a,b,c]">>, 1154: 1155: <<"H = qlc:q([X || X <- qlc:append([],qlc:append([[], []]))]), 1156: [] = qlc:e(H)">>, 1157: 1158: <<"Q1 = qlc:q([X || X <- [3,4,4]]), 1159: Q2 = qlc:q([X || X <- qlc:sort(qlc:append([[1,2], Q1]))]), 1160: [1,2,3,4,4] = qlc:e(Q2), 1161: [1,2,3,4] = qlc:e(Q2, {unique_all,true})">>, 1162: 1163: <<"[] = qlc:e(qlc:q([X || X <- qlc:append([])]))">>, 1164: 1165: <<"Q1 = qlc:q([X || X <- [a,b]]), 1166: Q2 = qlc:q([X || X <- [1,2]]), 1167: Q3 = qlc:append([Q1, Q2, qlc:sort([2,1])]), 1168: Q = qlc:q([X || X <- Q3]), 1169: {append, [{list, [a,b]}, 1170: {list, [1,2]}, 1171: {sort,{list, [2,1]},[]}]} = i(Q), 1172: [a,b,1,2,1,2] = qlc:e(Q)">> 1173: 1174: ], 1175: ?line run(Config, Ts), 1176: ok. 1177: 1178: evaluator(doc) -> 1179: "Simple call from evaluator."; 1180: evaluator(suite) -> []; 1181: evaluator(Config) when is_list(Config) -> 1182: ?line true = is_alive(), 1183: evaluator_2(Config, []), 1184: ?line {ok, Node} = start_node(qlc_SUITE_evaluator), 1185: ?line ok = rpc:call(Node, ?MODULE, evaluator_2, [Config, [compiler]]), 1186: ?line ?t:stop_node(Node), 1187: ok. 1188: 1189: evaluator_2(Config, Apps) -> 1190: ?line lists:foreach(fun(App) -> true = code:del_path(App) end, Apps), 1191: FileName = filename:join(?privdir, "eval"), 1192: ?line ok = file:write_file(FileName, 1193: <<"H = qlc:q([X || X <- L]), 1194: [1,2,3] = qlc:e(H).">>), 1195: ?line Bs = erl_eval:add_binding('L', [1,2,3], erl_eval:new_bindings()), 1196: ?line ok = file:eval(FileName, Bs), 1197: 1198: %% The error message is "handled" a bit too much... 1199: %% (no trace of erl_lint left) 1200: ?line ok = file:write_file(FileName, 1201: <<"H = qlc:q([X || X <- L]), qlc:e(H).">>), 1202: ?line {error,_} = file:eval(FileName), 1203: 1204: %% Ugly error message; badarg is caught by file.erl. 1205: ?line ok = file:write_file(FileName, 1206: <<"H = qlc:q([Z || {X,Y} <- [{a,2}], Z <- [Y]]), qlc:e(H).">>), 1207: ?line {error,_} = file:eval(FileName), 1208: 1209: _ = file:delete(FileName), 1210: ok. 1211: 1212: start_node(Name) -> 1213: ?line PA = filename:dirname(code:which(?MODULE)), 1214: ?t:start_node(Name, slave, [{args, "-pa " ++ PA}]). 1215: 1216: string_to_handle(doc) -> 1217: "string_to_handle/1,2."; 1218: string_to_handle(suite) -> []; 1219: string_to_handle(Config) when is_list(Config) -> 1220: ?line {'EXIT',{badarg,_}} = (catch qlc:string_to_handle(14)), 1221: ?line {'EXIT',{badarg,_}} = 1222: (catch qlc:string_to_handle("[X || X <- [a].", unique_all)), 1223: ?line R1 = {error, _, {_,erl_scan,_}} = qlc:string_to_handle("'"), 1224: ?line "1: unterminated " ++ _ = lists:flatten(qlc:format_error(R1)), 1225: ?line {error, _, {_,erl_parse,_}} = qlc:string_to_handle("foo"), 1226: ?line {'EXIT',{badarg,_}} = (catch qlc:string_to_handle("foo, bar.")), 1227: ?line R3 = {error, _, {_,?QLC,not_a_query_list_comprehension}} = 1228: qlc:string_to_handle("bad."), 1229: ?line "1: argument is not" ++ _ = lists:flatten(qlc:format_error(R3)), 1230: ?line R4 = {error, _, {_,?QLC,{used_generator_variable,'Y'}}} = 1231: qlc:string_to_handle("[X || begin Y = [1,2], true end, X <- Y]."), 1232: ?line "1: generated variable 'Y'" ++ _ = 1233: lists:flatten(qlc:format_error(R4)), 1234: ?line {error, _, {_,erl_lint,_}} = qlc:string_to_handle("[X || X <- A]."), 1235: ?line H1 = qlc:string_to_handle("[X || X <- [1,2]]."), 1236: ?line [1,2] = qlc:e(H1), 1237: ?line H2 = qlc:string_to_handle("[X || X <- qlc:append([a,b]," 1238: "qlc:e(qlc:q([X || X <- [c,d,e]])))]."), 1239: ?line [a,b,c,d,e] = qlc:e(H2), 1240: %% The generated fun has many arguments (erl_eval has a maximum of 20). 1241: ?line H3 = qlc:string_to_handle( 1242: "[{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} ||" 1243: " {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} <- []]."), 1244: ?line [] = qlc:e(H3), 1245: ?line Bs1 = erl_eval:add_binding('L', [1,2,3], erl_eval:new_bindings()), 1246: ?line H4 = qlc:string_to_handle("[X || X <- L].", [], Bs1), 1247: ?line [1,2,3] = qlc:e(H4), 1248: ?line H5 = qlc:string_to_handle("[X || X <- [1,2,1,2]].", [unique, cache]), 1249: ?line [1,2] = qlc:e(H5), 1250: 1251: ?line Ets = ets:new(test, []), 1252: ?line true = ets:insert(Ets, [{1}]), 1253: ?line Bs2 = erl_eval:add_binding('E', Ets, erl_eval:new_bindings()), 1254: ?line Q = "[X || {X} <- ets:table(E)].", 1255: ?line [1] = qlc:e(qlc:string_to_handle(Q, [], Bs2)), 1256: ?line [1] = qlc:e(qlc:string_to_handle(Q, {max_lookup,1000}, Bs2)), 1257: ?line [1] = qlc:e(qlc:string_to_handle(Q, {max_lookup,infinity}, Bs2)), 1258: ?line {'EXIT',{badarg,_}} = 1259: (catch qlc:string_to_handle(Q, {max_lookup,-1}, Bs2)), 1260: ?line {'EXIT', {no_lookup_to_carry_out, _}} = 1261: (catch qlc:e(qlc:string_to_handle(Q, {lookup,true}, Bs2))), 1262: ?line ets:delete(Ets), 1263: ok. 1264: 1265: table(doc) -> 1266: "table"; 1267: table(suite) -> []; 1268: table(Config) when is_list(Config) -> 1269: dets:start(), 1270: Ts = [ 1271: <<"E = ets:new(test, []), 1272: {'EXIT',{badarg,_}} = 1273: (catch qlc:e(qlc:q([X || X <- ets:table(E, [badarg])]))), 1274: [] = qlc:e(qlc:q([X || X <- ets:table(E)])), 1275: ets:delete(E)">>, 1276: 1277: <<"{'EXIT',{badarg,_}} = (catch qlc:table(not_a_fun, []))">>, 1278: 1279: <<"E = ets:new(test, []), 1280: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1281: H = qlc:q([{X,Y} || X <- ets:table(E), Y <- ets:table(E)]), 1282: R = qlc:e(H), 1283: ets:delete(E), 1284: [{{1,a},{1,a}},{{1,a},{2,b}},{{1,a},{3,c}}, 1285: {{2,b},{1,a}},{{2,b},{2,b}},{{2,b},{3,c}}, 1286: 1287: {{3,c},{1,a}},{{3,c},{2,b}},{{3,c},{3,c}}] = lists:sort(R)">>, 1288: 1289: <<"E = ets:new(test, []), 1290: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1291: H = qlc:q([X || X <- qlc:append([ets:table(E), [a,b,c], 1292: ets:table(E)])]), 1293: R = qlc:e(H), 1294: ets:delete(E), 1295: [a,b,c,{1,a},{1,a},{2,b},{2,b},{3,c},{3,c}] = lists:sort(R)">>, 1296: 1297: <<"E = ets:new(test, []), 1298: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1299: false = ets:info(E, safe_fixed), 1300: H = qlc:q([{X,Y} || X <- ets:table(E, {n_objects, default}), 1301: Y <- ets:table(E, {n_objects, 2}), 1302: false =/= ets:info(E, safe_fixed), 1303: throw({throw,apa})]), 1304: {throw,apa} = (catch {any_term,qlc:e(H)}), 1305: false = ets:info(E, safe_fixed), 1306: ets:delete(E)">>, 1307: 1308: <<"E = ets:new(test, []), 1309: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1310: false = ets:info(E, safe_fixed), 1311: H = qlc:q([{X,Y} || X <- ets:table(E), Y <- ets:table(E), 1312: false =/= ets:info(E, safe_fixed), exit(apa)]), 1313: {'EXIT',apa} = (catch {any_term,qlc:e(H)}), 1314: false = ets:info(E, safe_fixed), 1315: ets:delete(E)">>, 1316: 1317: <<"E = ets:new(test, []), 1318: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1319: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 1320: Y <- ets:table(E)]), 1321: R = (catch {any_term,qlc:cursor(H)}), 1322: false = ets:info(E, safe_fixed), 1323: ets:delete(E), 1324: {throw,bad_pre_fun} = R">>, 1325: 1326: <<"E = ets:new(test, []), 1327: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1328: H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 1329: Y <- ets:table(E)]), 1330: R = (catch {any_term,qlc:cursor(H)}), 1331: false = ets:info(E, safe_fixed), 1332: ets:delete(E), 1333: {'EXIT',{bad_pre_fun,_}} = R">>, 1334: 1335: <<"E = ets:new(test, [ordered_set]), 1336: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1337: H = qlc:q([X || X <- qlc_SUITE:default_table(E)]), 1338: R = qlc:e(H), 1339: ets:delete(E), 1340: [{1,a},{2,b},{3,c}] = R">>, 1341: 1342: <<"E = ets:new(test, [ordered_set]), 1343: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1344: H = qlc:q([X || X <- qlc_SUITE:bad_table(E)]), 1345: {'EXIT', {badarg, _}} = (catch qlc:e(H)), 1346: ets:delete(E)">>, 1347: 1348: %% The info tag num_of_objects is currently not used. 1349: % <<"E = ets:new(test, [ordered_set]), 1350: % true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1351: % H = qlc:q([X || X <- qlc_SUITE:bad_table_info_fun_n_objects(E)]), 1352: % {'EXIT', finito} = (catch {any_term,qlc:e(H)}), 1353: % ets:delete(E)">>, 1354: 1355: <<"E = ets:new(test, [ordered_set]), 1356: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1357: H = qlc:q([Y || {X,Y} <- qlc_SUITE:bad_table_info_fun_indices(E), 1358: X =:= a]), 1359: %% This is due to lookup. If the table were traversed there 1360: %% would be no failure. 1361: {throw, apa} = (catch {any_term,qlc:e(H)}), 1362: ets:delete(E)">>, 1363: 1364: <<"E = ets:new(test, [ordered_set]), 1365: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1366: H = qlc:q([Y || {X,Y} <- qlc_SUITE:bad_table_info_fun_keypos(E), 1367: X =:= a]), 1368: {'EXIT',{keypos,_}} = (catch {any_term,qlc:info(H)}), 1369: {'EXIT',{keypos,_}} = (catch {any_term,qlc:e(H)}), 1370: ets:delete(E)">>, 1371: 1372: begin 1373: MS = ets:fun2ms(fun(X) when element(1, X) > 1 -> X end), 1374: [<<"E = ets:new(test, []), 1375: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1376: MS = ">>, io_lib:format("~w", [MS]), <<", 1377: H = qlc:q([{X,Y} || X <- ets:table(E,{traverse,{select, MS}}), 1378: Y <- ets:table(E)]), 1379: R = qlc:e(H), 1380: ets:delete(E), 1381: [{{2,b},{1,a}},{{2,b},{2,b}},{{2,b},{3,c}}, 1382: {{3,c},{1,a}},{{3,c},{2,b}},{{3,c},{3,c}}] = lists:sort(R)">>] 1383: end, 1384: 1385: begin % a short table 1386: MS = ets:fun2ms(fun(X) when element(1, X) > 1 -> X end), 1387: [<<"E = ets:new(test, []), 1388: true = ets:insert(E, [{0,b}]), 1389: MS = ">>, io_lib:format("~w", [MS]), <<", 1390: H1 = qlc:q([X || X <- ets:table(E)]), 1391: R1 = qlc:e(H1), 1392: H2 = qlc:q([X || X <- ets:table(E, {traverse, {select, MS}})]), 1393: R2 = qlc:e(H2), 1394: ets:delete(E), 1395: [_] = R1, 1396: [] = R2">>] 1397: end, 1398: 1399: begin 1400: File = filename:join(?privdir, "detsfile"), 1401: _ = file:delete(File), 1402: [<<"{ok, Tab} = dets:open_file(apa, [{file,\"">>, File, <<"\"}, 1403: {type,bag}]), 1404: ok = dets:insert(Tab, [{1,a},{1,b}]), 1405: R = qlc:e(qlc:q([X || X <- dets:table(Tab)])), 1406: dets:close(Tab), 1407: file:delete(\"">>, File, <<"\"), 1408: R">>] 1409: end, 1410: 1411: %% [T || P <- Table, F] turned into a match spec. 1412: <<"E = ets:new(apa, [duplicate_bag]), 1413: true = ets:insert(E, [{1,a},{2,b},{3,c},{4,d}]), 1414: QH = qlc:q([X || {X,_} <- ets:table(E), X > 2], unique), 1415: {qlc, _, [{generate, _, {table, _}}], [{unique,true}]} = i(QH), 1416: [3,4] = lists:sort(qlc:e(QH)), 1417: ets:delete(E)">>, 1418: 1419: <<"E = ets:new(apa, []), 1420: true = ets:insert(E, [{1,a},{2,b}]), 1421: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_format(E)), 1422: ets:delete(E)">>, 1423: 1424: <<"E = ets:new(apa, []), 1425: true = ets:insert(E, [{1,a},{2,b}]), 1426: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_format_arity(E)), 1427: ets:delete(E)">>, 1428: 1429: <<"E = ets:new(apa, []), 1430: true = ets:insert(E, [{1,a},{2,b}]), 1431: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_info_arity(E)), 1432: ets:delete(E)">>, 1433: 1434: <<"E = ets:new(apa, []), 1435: true = ets:insert(E, [{1,a},{2,b}]), 1436: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_traverse(E)), 1437: ets:delete(E)">>, 1438: 1439: <<"E = ets:new(apa, []), 1440: true = ets:insert(E, [{1,a},{2,b}]), 1441: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_post(E)), 1442: ets:delete(E)">>, 1443: 1444: <<"E = ets:new(apa, []), 1445: true = ets:insert(E, [{1,a},{2,b}]), 1446: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_max_lookup(E)), 1447: ets:delete(E)">>, 1448: 1449: <<"E = ets:new(apa, []), 1450: true = ets:insert(E, [{1,a},{2,b}]), 1451: {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_lookup(E)), 1452: ets:delete(E)">>, 1453: 1454: <<"L = [{1,a},{2,b},{3,c}], 1455: QH = qlc:q([element(2, X) || X <- qlc_SUITE:table(L, [2]), 1456: (element(1, X) =:= 1) 1457: or (2 =:= element(1, X))]), 1458: [a,b] = lists:sort(qlc:e(QH))">>, 1459: 1460: <<"etsc(fun(E) -> 1461: Q = qlc:q([{A,B} || {A,B} <- 1462: qlc:q([{B,A} || {A,B} <- ets:table(E), 1463: (A =:= 1) or (A =:= 2), 1464: math:sqrt(B) < A])]), 1465: [{2,2}] = qlc:eval(Q), 1466: [1,2] = lookup_keys(Q) 1467: end, [{1,1},{2,2}])">> 1468: ], 1469: ?line run(Config, Ts), 1470: 1471: Ts2 = [ 1472: %% [T || P <- Table, F] turned into a match spec. Records needed. 1473: <<"E = ets:new(foo, [bag]), 1474: ets:insert(E, [{a,1,2},#a{b=3,c=4},{a,3}]), 1475: QH = qlc:q([X || X <- ets:table(E), is_record(X, a)]), 1476: {list,{table,_}, _} = i(QH), 1477: [{a,1,2},{a,3,4}] = lists:sort(qlc:eval(QH)), 1478: ets:delete(E)">> 1479: ], 1480: ?line run(Config, <<"-record(a, {b,c}).\n">>, Ts2), 1481: 1482: ok. 1483: 1484: process_dies(doc) -> 1485: "Caller or cursor process dies."; 1486: process_dies(suite) -> []; 1487: process_dies(Config) when is_list(Config) -> 1488: Ts = [ 1489: <<"E = ets:new(test, []), 1490: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1491: false = ets:info(E, safe_fixed), 1492: Parent = self(), 1493: F = fun() -> 1494: H = qlc:q([X || X <- ets:table(E)]), 1495: qlc:cursor(H), 1496: Parent ! {self(),ok} 1497: end, 1498: Pid = spawn_link(F), 1499: receive {Pid,ok} -> ok end, 1500: timer:sleep(10), 1501: false = ets:info(E, safe_fixed), 1502: ets:delete(E)">>, 1503: 1504: <<"%% This is not nice. The cursor's monitor kicks in. 1505: E = ets:new(test, []), 1506: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 1507: false = ets:info(E, safe_fixed), 1508: Parent = self(), 1509: F = fun() -> 1510: H = qlc:q([X || begin 1511: process_flag(trap_exit, false), 1512: {links, [Pid]} = 1513: process_info(self(), links), 1514: unlink(Pid), 1515: timer:sleep(1), 1516: {links, []} = 1517: process_info(self(), links), 1518: true 1519: end, 1520: X <- ets:table(E)]), 1521: C = qlc:cursor(H), 1522: qlc:next_answers(C), 1523: Parent ! {self(),ok} 1524: end, 1525: Pid = spawn_link(F), 1526: receive {Pid,ok} -> ok end, 1527: timer:sleep(10), 1528: false = ets:info(E, safe_fixed), 1529: ets:delete(E)">>, 1530: 1531: <<"H = qlc:q([X || X <- [1,2]]), 1532: {qlc_cursor, Term} = C = qlc:cursor(H), 1533: PF = process_flag(trap_exit, true), 1534: F = fun(T) -> not is_pid(T) end, 1535: [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)), 1536: exit(Pid, kill), 1537: timer:sleep(1), 1538: {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} = 1539: (catch qlc:next_answers(C)), 1540: process_flag(trap_exit, PF)">>, 1541: <<"H = qlc:q([X || begin process_flag(trap_exit, true), true end, 1542: X <- [1,2]]), 1543: {qlc_cursor, Term} = C = qlc:cursor(H), 1544: PF = process_flag(trap_exit, true), 1545: F = fun(T) -> not is_pid(T) end, 1546: [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)), 1547: [1] = qlc:next_answers(C, 1), 1548: exit(Pid, stop), 1549: timer:sleep(1), 1550: {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} = 1551: (catch qlc:next_answers(C)), 1552: process_flag(trap_exit, PF)">>, 1553: <<"%% This is not nice. No cleanup is done... 1554: H = qlc:q([X || begin process_flag(trap_exit, false), true end, 1555: X <- [1,2]]), 1556: {qlc_cursor, Term} = C = qlc:cursor(H), 1557: PF = process_flag(trap_exit, true), 1558: F = fun(T) -> not is_pid(T) end, 1559: [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)), 1560: [1] = qlc:next_answers(C, 1), 1561: exit(Pid, stop), 1562: timer:sleep(1), 1563: {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} = 1564: (catch qlc:next_answers(C)), 1565: process_flag(trap_exit, PF)">>, 1566: 1567: <<"PF = process_flag(trap_exit, true), 1568: E = ets:new(test, []), 1569: %% Hard kill. No cleanup will be done. 1570: H = qlc:q([X || begin exit(self(), kill), true end, 1571: X <- ets:table(E)]), 1572: C = qlc:cursor(H), 1573: {'EXIT', {{qlc_cursor_pid_no_longer_exists, _}, _}} = 1574: (catch qlc:next_answers(C)), 1575: false = ets:info(E, safe_fixed), % - but Ets cleans up anyway. 1576: true = ets:delete(E), 1577: process_flag(trap_exit, PF)">>, 1578: 1579: <<"E = ets:new(test, []), 1580: true = ets:insert(E, [{1,a}]), 1581: %% The signal is caught by trap_exit. No process dies... 1582: H = qlc:q([X || begin exit(self(), normal), true end, 1583: X <- ets:table(E)]), 1584: C = qlc:cursor(H, {spawn_options, []}), 1585: [{1,a}] = qlc:next_answers(C), 1586: qlc:delete_cursor(C), 1587: false = ets:info(E, safe_fixed), 1588: true = ets:delete(E)">>, 1589: <<"E = ets:new(test, []), 1590: true = ets:insert(E, [{1,a}]), 1591: %% The same as last example. 1592: H = qlc:q([X || begin 1593: process_flag(trap_exit, true), 1594: exit(self(), normal), true 1595: end, 1596: X <- ets:table(E)]), 1597: C = qlc:cursor(H, {spawn_options, []}), 1598: [{1,a}] = qlc:next_answers(C), 1599: qlc:delete_cursor(C), 1600: false = ets:info(E, safe_fixed), 1601: true = ets:delete(E), ok">>, 1602: <<"E = ets:new(test, []), 1603: true = ets:insert(E, [{1,a}]), 1604: H = qlc:q([X || X <- ets:table(E)]), 1605: SpawnOpts = [link, monitor], % monitor is ignored 1606: {qlc_cursor, Term} = C = qlc:cursor(H, {spawn_options, SpawnOpts}), 1607: F = fun(T) -> not is_pid(T) end, 1608: [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)), 1609: Me = self(), 1610: qlc_SUITE:install_error_logger(), 1611: Tuple = {this, tuple, is, writton, onto, the, error_logger}, 1612: SP = spawn(fun() -> 1613: Pid ! Tuple, 1614: Me ! {self(), done} 1615: end), 1616: receive {SP, done} -> ok end, 1617: [{1,a}] = qlc:next_answers(C), 1618: qlc:delete_cursor(C), 1619: {error, _Pid, Tuple} = qlc_SUITE:read_error_logger(), 1620: qlc_SUITE:uninstall_error_logger(), 1621: false = ets:info(E, safe_fixed), 1622: true = ets:delete(E), ok">> 1623: 1624: ], 1625: ?line run(Config, Ts), 1626: ok. 1627: 1628: sort(doc) -> 1629: "The sort option."; 1630: sort(suite) -> []; 1631: sort(Config) when is_list(Config) -> 1632: Ts = [ 1633: <<"H = qlc:q([X || X <- qlc:sort([1,2,3,2], {unique,true})]), 1634: [1,2,3] = qlc:e(H), 1635: C1 = qlc:cursor(H), 1636: [1,2,3] = qlc:next_answers(C1, all_remaining), 1637: qlc:delete_cursor(C1)">>, 1638: 1639: <<"H = qlc:q([{X,Y} || X <- qlc:sort(qlc:q([X || X <- [1,2,3,2]]), 1640: {unique,true}), 1641: Y <- [a,b]]), 1642: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H), 1643: C = qlc:cursor(H), 1644: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 1645: qlc:next_answers(C, all_remaining), 1646: qlc:delete_cursor(C)">>, 1647: 1648: <<"H = qlc:q([X || X <- qlc:sort(qlc:q([X || X <- apa]))]), 1649: {'EXIT',{badarg,_}} = (catch qlc:e(H))">>, 1650: 1651: %% An example with a side effect. The result may vary... 1652: <<"E = ets:new(test, [duplicate_bag]), 1653: true = ets:insert(E, [{1,17},{1,a}]), 1654: H_1 = qlc:q([X || X <- ets:table(E)]), 1655: H = qlc:q([X || X <- [1,2,3], ets:insert(E, {1,-X}), 1656: {_,Y} <- H_1, 1657: X > Y]), 1658: true = lists:sort(qlc:e(H)) == [1,2,2,3,3,3], 1659: true = ets:delete(E)">>, 1660: 1661: <<"E = ets:new(test, [duplicate_bag]), 1662: true = ets:insert(E, [{1,17}]), 1663: H_1 = qlc:q([X || X <- qlc:sort(ets:tab2list(E))]), 1664: %% Note: side effect in filter! 1665: H = qlc:q([X || X <- [1,2,3], ets:insert(E, {1,-X}), 1666: {_,Y} <- H_1, X > Y]), 1667: [] = qlc:e(H), 1668: true = ets:delete(E)">>, 1669: 1670: <<"H = qlc:q([X || X <- qlc:sort([1,2,3], {fopp,la})]), 1671: {'EXIT',{badarg,_}} = (catch qlc:e(H)), 1672: {'EXIT',{badarg,_}} = (catch qlc:cursor(H)), 1673: F = fun(Obj, A) -> A++[Obj] end, 1674: {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], H))">>, 1675: 1676: <<"Q1 = qlc:q([X || X <- [1,2]]), 1677: AL = [Q1, [1,2,3]], 1678: Q2 = qlc:q([X || X <- qlc:sort(qlc:append(AL))]), 1679: [1,1,2,2,3] = qlc:e(Q2)">>, 1680: 1681: <<"H = qlc:q([{X,Y} || X <- qlc:sort(qlc:q([X || X <- [1,2,3,2]]), 1682: {unique,true}), 1683: Y <- [a,b]]), 1684: {qlc, _, 1685: [{generate, _, {sort, {qlc, _, [{generate, _, {list, [1,2,3,2]}}], 1686: [{unique,true}]}, 1687: []}}, 1688: {generate, _, {qlc, _, [{generate, _, {list, [a,b]}}], 1689: [{unique,true}]}}], 1690: [{unique,true}]} = i(H, unique_all), 1691: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H, unique_all)">>, 1692: 1693: <<"L = [1,2,1,3,4,3,1], 1694: true = lists:sort(L) == qlc:e(qlc:q([X || X <- qlc:sort(L)])), 1695: true = lists:usort(L) == 1696: qlc:e(qlc:q([X || X <- qlc:sort(L, {unique,true})])), 1697: true = lists:reverse(lists:sort(L)) == 1698: qlc:e(qlc:q([X || X <- qlc:sort(L, {order, descending})])), 1699: true = lists:reverse(lists:usort(L)) == 1700: qlc:e(qlc:q([X || X <- qlc:sort(L, [{order, descending}, 1701: {unique, true}])])), 1702: CF = fun(X, Y) -> X =< Y end, 1703: true = lists:sort(L) == 1704: qlc:e(qlc:q([X || X <- qlc:sort(L, {order, CF})])), 1705: true = lists:usort(L) == 1706: qlc:e(qlc:q([X || X <- qlc:sort(L, [{order, CF}, 1707: {unique, true}])]))">>, 1708: 1709: <<"E = ets:new(foo, []), 1710: [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})], 1711: H = qlc:q([{X,Y} || X <- [a,b], Y <- qlc:sort(ets:table(E))]), 1712: 100000 = length(qlc:e(H)), 1713: ets:delete(E)">>, 1714: 1715: begin 1716: TmpDir = ?privdir, 1717: [<<"TE = process_flag(trap_exit, true), 1718: E = ets:new(foo, []), 1719: [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})], 1720: Ports = erlang:ports(), 1721: H = qlc:q([{X,Y} || X <- [a,b], 1722: begin 1723: [P] = erlang:ports() -- Ports, 1724: exit(P, port_exit), 1725: true 1726: end, 1727: Y <- qlc:sort(ets:table(E), 1728: [{tmpdir,\"">>, 1729: TmpDir, <<"\"}])]), 1730: {error, qlc, {file_error, _, _}} = (catch qlc:e(H)), 1731: receive {'EXIT', _, port_exit} -> ok end, 1732: ets:delete(E), 1733: process_flag(trap_exit, TE)">>] 1734: end 1735: 1736: ], 1737: ?line run(Config, Ts), 1738: ok. 1739: 1740: keysort(doc) -> 1741: "The sort option."; 1742: keysort(suite) -> []; 1743: keysort(Config) when is_list(Config) -> 1744: 1745: Ts = [ 1746: <<"OF = fun(X, Y) -> X =< Y end, 1747: F = fun(Obj, A) -> A++[Obj] end, 1748: H = qlc:q([X || X <- qlc:keysort(1, [{2,a},{1,b}], {order,OF})]), 1749: {'EXIT',{{badarg,order},_}} = (catch qlc:e(H)), 1750: {'EXIT',{{badarg,order},_}} = (catch qlc:fold(F, [], H)), 1751: {'EXIT',{{badarg,order},_}} = (catch qlc:cursor(H))">>, 1752: 1753: <<"E = create_ets(1, 2), 1754: H = qlc:q([X || X <- qlc:keysort([1], ets:table(E), 1755: [{size,1},{tmpdir,\"/a/b/c\"}])]), 1756: H1 = qlc:q([X || {X,_} <- qlc:e(H), X < 4]), 1757: {error,_,{file_error,_,_}} = qlc:info(H1), 1758: {error,_,{file_error,_,_}} = qlc:e(H1), 1759: ets:delete(E)">>, 1760: 1761: <<"L = [{1,a},{2,b},{3,c},{2,b}], 1762: H = qlc:q([{X,Y} || {X,_} <- qlc:keysort(1, qlc:q([X || X <- L]), 1763: {unique,true}), 1764: Y <- [a,b]]), 1765: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H), 1766: C = qlc:cursor(H), 1767: [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 1768: qlc:next_answers(C, all_remaining), 1769: qlc:delete_cursor(C)">>, 1770: 1771: <<"H1 = qlc:q([X || X <- qlc:keysort(0, [])]), 1772: {'EXIT',{badarg,_}} = (catch qlc:e(H1)), 1773: H2 = qlc:q([X || X <- qlc:keysort(1, [], {bad,arg})]), 1774: {'EXIT',{badarg,_}} = (catch qlc:e(H2)), 1775: H3 = qlc:q([X || X <- qlc:keysort([], [])]), 1776: {'EXIT',{badarg,_}} = (catch qlc:e(H3))">>, 1777: 1778: <<"H = qlc:q([X || X <- qlc:keysort(1, [{1,a},{2,b}], 1779: [{order,descending}, 1780: {compressed,true}])]), 1781: [{2,b},{1,a}] = qlc:e(H), 1782: H2 = qlc:q([X || X <- qlc:keysort(1, [{1},{2}], compressed)]), 1783: {'EXIT', {badarg, _}} = (catch qlc:e(H2))">>, 1784: 1785: <<"H = qlc:q([X || X <- qlc:keysort(1, [{1,a},{2,b}], {compressed,false})]), 1786: [{1,a},{2,b}] = qlc:e(H)">>, 1787: 1788: <<"E = create_ets(1, 2), 1789: H = qlc:q([X || X <- qlc:keysort([1], ets:table(E), 1790: [{size,1},{tmpdir,\"/a/b/c\"}])]), 1791: F = fun(Obj, A) -> A++[Obj] end, 1792: {error,_,{file_error,_,_}} = qlc:e(H), 1793: \" \\\"no such\" ++ _ = lists:dropwhile(fun(A) -> A =/= $\s end, 1794: lists:flatten(qlc:format_error(qlc:e(H)))), 1795: {error,_,{file_error,_,_}} = qlc:e(H, {unique_all,true}), 1796: {error,_,{file_error,_,_}} = qlc:cursor(H), 1797: {error,_,{file_error,_,_}} = qlc:cursor(H, {unique_all,true}), 1798: {error,_,{file_error,_,_}} = qlc:cursor(H, {spawn_options, []}), 1799: {error,_,{file_error,_,_}} = qlc:cursor(H, {spawn_options,default}), 1800: {error,_,{file_error,_,_}} = 1801: qlc:cursor(H, [{unique_all,true},{spawn_options, []}]), 1802: {error,_,{file_error,_,_}} = qlc:fold(F, [], H), 1803: {error,_,{file_error,_,_}} = qlc:fold(F, [],H, {unique_all,true}), 1804: ets:delete(E)">>, 1805: 1806: <<"L = [{1,b,a},{1,b,b},{1,a,a}], 1807: H = qlc:q([X || X <- qlc:keysort([4,1], L)]), 1808: {error,_,bad_object} = qlc:e(H), 1809: \"the keys\" ++ _ = qlc:format_error(qlc:e(H))">>, 1810: 1811: begin 1812: File = filename:join(?privdir, "afile"), 1813: ok = file:write_file(File, <<>>), 1814: [<<"H = qlc:q([X || X <- qlc:keysort([1], [{1},{2},{1}], 1815: [{tmpdir,\"">>, File, <<"\"}, 1816: {size,1}])]), 1817: {error,_,{file_error,_,_}} = qlc:e(H), 1818: file:delete(\"">>, File, <<"\")">>] 1819: end, 1820: 1821: <<"H0 = qlc:q([X || X <- [1,2,3]]), 1822: H = qlc:q([X || X <- qlc:sort(H0,{tmpdir,\".\"})]), 1823: [1,2,3] = qlc:e(H)">>, 1824: 1825: %% The global option 'tmpdir' takes precedence. 1826: begin 1827: PrivDir = ?privdir, 1828: [<<"L = [{1,a},{2,b}], 1829: H = qlc:q([X || X <- qlc:keysort([1], L, {tmpdir,\"/a/b/c\"})]), 1830: H1 = qlc:q([X || X <- H, X > 3]), 1831: Dir = \"">>, PrivDir, <<"\", 1832: Options = [{tmpdir, Dir}], 1833: {qlc,_,[{generate,_,{keysort,{list,L},[1],[{tmpdir,Dir}]}},_],[]} = 1834: i(H1, Options), 1835: [{1,a},{2,b}] = qlc:e(H1, Options)">>] % no check of "/a/b/c" 1836: end, 1837: 1838: <<"L = [{2,c},{1,b},{1,a},{3,a},{1,c},{2,b}], 1839: true = lists:sort(L) == 1840: qlc:e(qlc:q([X || X <- qlc:keysort([1,2], L)]))">>, 1841: 1842: <<"L = [{1,b},{2,c},{1,a},{3,e},{4,f},{3,d}], 1843: true = lists:keysort(1, L) == 1844: qlc:e(qlc:q([X || X <- qlc:keysort(1,L)])), 1845: true = lists:ukeysort(1, L) == 1846: qlc:e(qlc:q([X || X <- qlc:keysort(1, L, {unique,true})])), 1847: true = lists:reverse(lists:keysort(1, L)) == 1848: qlc:e(qlc:q([X || X <- qlc:keysort(1,L, 1849: {order, descending})])), 1850: true = lists:reverse(lists:ukeysort(1, L)) == 1851: qlc:e(qlc:q([X || X <- qlc:keysort(1, L, [{unique,true}, 1852: {order, descending}])]))">>, 1853: 1854: <<"L = [{X} || X <- lists:seq(1,100000)], 1855: H1 = qlc:append([L,[{1,2},{2,3},{3,4}]]), 1856: H = qlc:keysort([1], qlc:keysort([1], H1, [{compressed,true}])), 1857: R = qlc:e(H), 1858: 100003 = length(R)">> 1859: 1860: ], 1861: ?line run(Config, Ts), 1862: 1863: ok. 1864: 1865: filesort(doc) -> 1866: "keysort/1,2, using a file."; 1867: filesort(suite) -> []; 1868: filesort(Config) when is_list(Config) -> 1869: Ts = [ 1870: <<"Q = qlc:q([X || X <- [{3},{1},{2}]]), 1871: Opts = [{size,10},{no_files,3}], 1872: Q2 = qlc:q([{X,Y} || Y <- [1,2], X <- qlc:keysort([1],Q,Opts)]), 1873: [{{1},1},{{2},1},{{3},1},{{1},2},{{2},2},{{3},2}] = qlc:e(Q2)">> 1874: ], 1875: ?line run(Config, Ts), 1876: ok. 1877: 1878: 1879: cache(doc) -> 1880: "The cache option."; 1881: cache(suite) -> []; 1882: cache(Config) when is_list(Config) -> 1883: Ts = [ 1884: <<"{'EXIT', {badarg, _}} = (catch qlc:q([X || X <- [1,2]], badarg))">>, 1885: 1886: <<"Q1 = qlc:q([X || X <- [1,2,1,2,1]], {unique,true}), 1887: [1,2] = qlc:e(Q1), 1888: [1,2] = qlc:e(Q1, {unique_all,true}), 1889: Q2 = qlc:q([X || X <- qlc:q([X || X <- [1,2,1,2,1]], 1890: {unique,true})]), 1891: [1,2] = qlc:e(Q2), 1892: [1,2] = qlc:e(Q2, {unique_all,true}), 1893: Q3 = qlc:q([X || X <- qlc:append([[1,2,3], [4,5,6]])]), 1894: [1,2,3,4,5,6] = qlc:e(Q3)">>, 1895: 1896: <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{2,a},{1,b},{2,b}]]), 1897: Q2 = qlc:q([{X,make_ref()} || X <- Q1]), 1898: [{1,_},{2,_},{1,_},{2,_}] = qlc:e(Q2, {unique_all,false}), 1899: [{1,_},{2,_}] = qlc:e(Q2, {unique_all,true})">>, 1900: 1901: <<"E = ets:new(test, [duplicate_bag]), 1902: true = ets:insert(E, [{1,a},{2,a},{1,b},{2,b}]), 1903: Q1 = qlc:q([X || {X,_} <- ets:table(E)]), 1904: Q2 = qlc:q([{X,make_ref()} || X <- Q1]), 1905: [{1,_},{1,_},{2,_},{2,_}] = lists:sort(qlc:e(Q2, {unique_all,false})), 1906: [{1,_},{2,_}] = lists:sort(qlc:e(Q2, {unique_all,true})), 1907: ets:delete(E)">>, 1908: 1909: <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{2,a},{1,b},{2,b}]]), 1910: Q2 = qlc:q([{X,make_ref()} || X <- qlc:append([Q1, Q1])]), 1911: [{1,_},{2,_},{1,_},{2,_},{1,_},{2,_},{1,_},{2,_}] = 1912: qlc:e(Q2, {unique_all,false}), 1913: [{1,_},{2,_}] = qlc:e(Q2, {unique_all,true})">>, 1914: 1915: <<"[] = qlc:e(qlc:q([X || X <- []], {unique, true})), 1916: [] = qlc:e(qlc:q([X || X <- qlc:q([X || X <- qlc:append([])], 1917: {unique,true})]))">>, 1918: 1919: <<"Q1 = qlc:q([{X,make_ref()} || {X,_} <- [{1,a},{1,b}]]), 1920: [{1,_},{1,_}] = qlc:e(Q1, {unique_all, true}), 1921: Q2 = qlc:q([Y || {X,_} <- [{1,a},{1,b}], 1922: begin Y = {X,make_ref()}, true end]), 1923: [{1,_},{1,_}] = qlc:e(Q2, {unique_all,true}), 1924: Q3 = qlc:q([Y || X <- [{1,a},{2,a}], 1925: begin {_,Z} = X, Y = {Z,make_ref()}, true end]), 1926: [{a,_},{a,_}] = qlc:e(Q3, {unique_all, true})">>, 1927: 1928: <<"E = ets:new(apa, [duplicate_bag]), 1929: ets:insert(E, [{1,a},{2,a},{1,a}]), 1930: H1 = qlc:q([X || X <- qlc:append(ets:table(E),[7,3,5])], 1931: {cache, true}), 1932: [{_,a},{_,a},{_,a},7,3,5] = qlc:e(H1), 1933: ets:delete(E)">>, 1934: 1935: <<"F = fun(Obj, A) -> A++[Obj] end, 1936: H = qlc:q([X || X <- [1,3,2,4]], cache), 1937: Q = qlc:q([X || X <- H]), 1938: [1,3,2,4] = qlc:fold(F, [], Q, [])">>, 1939: 1940: <<"F = fun(Obj, A) -> A++[Obj] end, 1941: E = ets:new(apa, [duplicate_bag]), 1942: true = ets:insert(E, [{1,a},{2,b},{1,a}]), 1943: Q1 = qlc:q([X || X <- ets:table(E)], [cache, unique]), 1944: Q = qlc:q([X || X <- Q1], [cache, unique]), 1945: {qlc, _, [{generate, _,{table,_}}], [{unique,true}]} = i(Q), 1946: R = qlc:fold(F, [], Q, []), 1947: ets:delete(E), 1948: true = [{1,a},{2,b}] == lists:sort(R)">>, 1949: 1950: <<"E = ets:new(apa, [duplicate_bag]), 1951: ets:insert(E, [{1,a},{2,b},{1,a}]), 1952: H1 = qlc:q([X || X <- qlc:append(ets:table(E),[7,3])], cache), 1953: H2 = qlc:q([{Y,X} || Y <- [2,1,3], X <- H1]), 1954: [{2,_},{2,_},{2,_},{2,7},{2,3}, 1955: {1,_},{1,_},{1,_},{1,7},{1,3}, 1956: {3,_},{3,_},{3,_},{3,7},{3,3}] = qlc:e(H2), 1957: ets:delete(E)">>, 1958: 1959: %% This case is not 100 percent determined. An Ets table 1960: %% is updated in a filter and later used in a generator. 1961: <<"E = ets:new(apa, [bag]), 1962: true = ets:insert(E, [{1,a},{2,b}]), 1963: H1 = qlc:q([Y || Y <- ets:table(E)], 1964: [{cache, no}, {unique, true}]), 1965: H = qlc:q([{X,Y} || X <- [{1,a},{2,d},{3,e}], 1966: ets:insert(E, X), 1967: Y <- H1]), 1968: [{{1,a},_}, {{1,a},_}, {{2,d},_}, {{2,d},_}, {{2,d},_}, 1969: {{3,e},_}, {{3,e},_}, {{3,e},_}, {{3,e},_}] = qlc:e(H), 1970: ets:delete(E)">>, 1971: 1972: %% This case is not 100 percent determined. An Ets table 1973: %% is updated in a filter and later used in a generator. 1974: <<"E = ets:new(apa, [bag]), 1975: true = ets:insert(E, [{1,a},{2,b}]), 1976: H1 = qlc:q([Y || Y <- ets:table(E)], 1977: [{cache, true}, {unique, true}]), 1978: H = qlc:q([{X,Y} || X <- [{1,a},{2,d},{3,e}], 1979: ets:insert(E, X), 1980: Y <- H1]), 1981: [{{1,a},_}, {{1,a},_}, {{2,d},_}, {{2,d},_}, {{3,e},_}, {{3,e},_}] = 1982: qlc:e(H), 1983: ets:delete(E)">>, 1984: 1985: <<"%% {5979} and {5549} are both hashed to 28244 by phash2/1 1986: E = ets:new(apa, [duplicate_bag]), 1987: true = ets:insert(E, [{5979},{5549},{5979},{5549},{0}]), 1988: H1 = qlc:q([X || X <- ets:table(E)], 1989: [{cache, true}, {unique, true}]), 1990: H = qlc:q([Y || _ <- [1,2], Y <- H1]), 1991: {qlc, _, [{generate, _, {list, [1,2]}}, 1992: {generate, _, {qlc, _, [{generate, _, {table,_}}], 1993: [{cache,ets},{unique,true}]}}], 1994: []} = i(H), 1995: [{0},{0},{5549},{5549},{5979},{5979}] = lists:sort(qlc:e(H)), 1996: ets:delete(E)">>, 1997: 1998: <<"E = ets:new(apa, [ordered_set]), 1999: ets:insert(E, [{1},{2}]), 2000: H1 = qlc:q([X || X <- ets:table(E)], [cache, unique]), 2001: H2 = qlc:q([X || Y <- [3,4], ets:insert(E, {Y}), X <- H1]), 2002: {qlc, _, [{generate, _, {list, [3,4]}}, _, 2003: {generate, _, {qlc, _, [{generate, _, 2004: {table, _}}], 2005: [{cache, ets}]}}], 2006: []} = i(H2), 2007: [{1},{2},{3},{1},{2},{3}] = qlc:e(H2), 2008: ets:delete(E)">>, 2009: 2010: <<"E = ets:new(apa, [ordered_set]), 2011: ets:insert(E, [{1},{2}]), 2012: H1 = qlc:q([X || X <- ets:table(E)], [unique]), 2013: H2 = qlc:q([X || Y <- [3,4], ets:insert(E, {Y}), X <- H1]), 2014: [{1},{2},{3},{1},{2},{3},{4}] = qlc:e(H2), 2015: ets:delete(E)">>, 2016: 2017: <<"H0 = qlc:append([a,b], [c,d]), 2018: H = qlc:q([{X,Y} || 2019: X <- H0, 2020: Y <- qlc:q([{X1,Y} || 2021: X1 <- H0, 2022: Y <- qlc:q([{X2,Y} || 2023: X2 <- H0, 2024: Y <- H0])])]), 2025: {qlc, _, 2026: [{generate, _,{append, [{list, [a,b]}, {list, [c,d]}]}}, 2027: {generate, _, 2028: {qlc, _, 2029: [{generate, _, {append,[{list, [a,b]},{list, [c,d]}]}}, 2030: {generate, _, 2031: {qlc, _, 2032: [{generate, _,{append,[{list, [a,b]}, {list, [c,d]}]}}, 2033: {generate, _,{append,[{list, [a,b]}, {list, [c,d]}]}}], 2034: [{cache,ets}]}}], 2035: [{cache,ets}]}}], 2036: []} = i(H, cache_all)">> 2037: 2038: ], 2039: ?line run(Config, Ts), 2040: ok. 2041: 2042: cache_list(doc) -> 2043: "OTP-6038. The {cache,list} option."; 2044: cache_list(suite) -> []; 2045: cache_list(Config) when is_list(Config) -> 2046: Ts = [ 2047: begin 2048: PrivDir = ?privdir, 2049: [<<"%% unique, cache list. A simple qlc. 2050: Options = [{cache,list}, unique], 2051: L0 = [1,2,3,4,1,2,3,4], 2052: L = qlc_SUITE:table(L0, []), 2053: Q1 = qlc:q([X || X <- L], Options), 2054: Q = qlc:q([{X,Y} || X <- [a,b], Y <- Q1]), 2055: GOptions = [{tmpdir,\"">>, PrivDir, <<"\"}], 2056: {qlc,_,[{generate,_,{list,[a,b]}}, 2057: {generate,_,{qlc,_, 2058: [{generate,_,{table,_}}], 2059: [{cache,list},{unique,true}]}}], 2060: []} = i(Q, GOptions), 2061: true = [{X,Y} || X <- [a,b], Y <- [1,2,3,4]] =:= 2062: qlc:e(Q, GOptions)">>] 2063: end, 2064: 2065: begin 2066: MS = ets:fun2ms(fun({X,_}) when X > 1 -> X end), 2067: [<<"%% No cache, even if explicit match specification. 2068: etsc(fun(E) -> 2069: MS = ">>, io_lib:format("~w", [MS]), <<", 2070: Options = [{cache,list}, unique], 2071: Q = qlc:q([{X,Y} || 2072: X <- ets:table(E, {traverse, {select, MS}}), 2073: Y <- [1,2,3]], 2074: Options), 2075: {qlc,_,[{generate,_,{table,{ets,table,_}}}, 2076: {generate,_,{list,[1,2,3]}}], 2077: [{unique,true}]} = i(Q), 2078: true = [{X,Y} || X <- lists:seq(2,10), Y <- [1,2,3]] =:= 2079: lists:sort(qlc:e(Q)) 2080: end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>] 2081: end, 2082: 2083: <<"%% Temporary files. 2084: %% Remove list expression caches when possible. (no visible effect) 2085: T = lists:seq(1, 100000), % Huge terms on file 2086: F = fun(C) -> 2087: Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 2088: {cache,C}), 2089: Q1 = qlc:q([{X,Y,Z} || 2090: X <- Q0, 2091: Y <- Q0, 2092: Z <- Q0], 2093: {cache,C}), 2094: qlc:q([{X, Y} || Y <- [1], X <- Q1]) 2095: end, 2096: Ql = F(list), 2097: Rl = qlc:e(Ql, {max_list_size, 64*1024}), 2098: Qe = F(ets), 2099: Re = qlc:e(Qe), 2100: Qf = F(no), 2101: Rf = qlc:e(Qf), 2102: Ri = qlc:e(Ql, {max_list_size, 1 bsl 35}), % heavy 2103: {27,27,27,27,true,true,true} = 2104: {length(Rl), length(Re), length(Rf), length(Ri), 2105: Rl =:= Re, Re =:= Rf, Rf =:= Ri}">>, 2106: 2107: <<"%% File sorter. 2108: T = lists:seq(1, 10000), 2109: F = fun(C) -> 2110: Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 2111: [{cache,C},unique]), 2112: Q1 = qlc:q([{X,Y,Z} || 2113: X <- Q0, 2114: Y <- Q0, 2115: Z <- Q0], 2116: [{cache,C},unique]), 2117: qlc:q([{X, Y} || Y <- [1], X <- Q1]) 2118: end, 2119: GOpt = [{max_list_size, 10000}], 2120: Ql = F(list), 2121: Rl = qlc:e(Ql, GOpt), 2122: Qe = F(ets), 2123: Re = qlc:e(Qe, GOpt), 2124: Qf = F(no), 2125: Rf = qlc:e(Qf, GOpt), 2126: {1,1,1,true,true} = 2127: {length(Rl), length(Re), length(Rf), Rl =:= Re, Re =:= Rf}">>, 2128: 2129: <<"%% Remove list expression caches when possible. (no visible effect) 2130: Q0 = qlc:q([{X} || X <- [1,2,3], begin X > 0 end], {cache,list}), 2131: Q1 = qlc:q([{X,Y,Z} || 2132: X <- Q0, 2133: Y <- Q0, 2134: Z <- Q0], 2135: {cache,list}), 2136: Q = qlc:q([{X, Y} || Y <- [1], X <- Q1]), 2137: R = qlc:e(Q), 2138: L0 = [{X} || X <- [1,2,3], begin X > 0 end], 2139: L1 = [{X,Y,Z} || 2140: X <- L0, 2141: Y <- L0, 2142: Z <- L0], 2143: L = [{X, Y} || Y <- [1], X <- L1], 2144: true = R =:= L">>, 2145: 2146: <<"%% No temporary file. 2147: L = [{I,a} || I <- lists:seq(1, 10)], 2148: Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err), 2149: begin element(1, X) > 5 end], 2150: {cache,list}), 2151: Q = qlc:q([{X, element(1,Y)} || 2152: X <- lists:seq(1, 5), 2153: Y <- Q0]), 2154: err = qlc:e(Q)">>, 2155: 2156: <<"%% Sort internally. 2157: L = [{I,a} || I <- lists:seq(1, 10)], 2158: Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err), 2159: begin element(1, X) > 5 end], 2160: [unique,{cache,list}]), 2161: Q = qlc:q([{X, element(1,Y)} || 2162: X <- lists:seq(1, 5), 2163: Y <- Q0]), 2164: err = qlc:e(Q, {max_list_size,0})">>, 2165: 2166: <<"%% No temporary file. 2167: etsc(fun(E) -> 2168: Q0 = qlc:q([X || X <- ets:table(E), 2169: begin element(1, X) > 5 end], 2170: {cache,list}), 2171: Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5), 2172: Y <- Q0]), 2173: R = [{X,Y} || X <- lists:seq(1, 5), 2174: Y <- lists:seq(6, 10)], 2175: R = lists:sort(qlc:e(Q)) 2176: end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>, 2177: 2178: <<"%% Sort internally 2179: etsc(fun(E) -> 2180: Q0 = qlc:q([X || X <- ets:table(E), 2181: begin element(1, X) > 5 end], 2182: [{cache,list},unique]), 2183: Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5), 2184: Y <- Q0]), 2185: R = [{X,Y} || X <- lists:seq(1, 5), 2186: Y <- lists:seq(6, 10)], 2187: R = lists:sort(qlc:e(Q)) 2188: end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>, 2189: 2190: <<"%% A few more tests of unique and {cache,list}. 2191: F = fun(CU) -> 2192: H1 = qlc:q([{X,Y} || 2193: Y <- [a,b], 2194: X <- [1,2]], 2195: CU), 2196: qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]) 2197: end, 2198: Q1 = F([]), 2199: Q2 = F([{cache,list}, unique]), 2200: R1 = qlc:e(Q1), 2201: R2 = qlc:e(Q2), 2202: R3 = qlc:e(Q2, {max_list_size, 0}), % still in RAM 2203: true = R1 =:= R2, 2204: true = R2 =:= R3">>, 2205: 2206: <<"E = ets:new(t, [duplicate_bag]), 2207: true = ets:insert(E, [{2},{1},{2}]), 2208: H1 = qlc:q([{X,Y} || 2209: Y <- [a,b], 2210: {X} <- ets:table(E)], 2211: [{cache,list}, unique]), 2212: H2 = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]), 2213: {qlc,_,[{generate,_,{list,[3,4]}}, 2214: {generate,_,{qlc,_, 2215: [{generate,_,{list,[a,b]}}, 2216: {generate,_, 2217: {qlc,_,[{generate,_,{table,{ets,table,_}}}], 2218: [{cache,list},{unique,true}]}}], 2219: [{cache,list},{unique,true}]}}], []} = i(H2), 2220: L1s = [[{X,Y} || Y <- [a,b], X <- Xs] || Xs <- [[1,2], [2,1]]], 2221: L2s = [[{X,Y,Z} || X <- [3,4], {Y,Z} <- L1] || L1 <- L1s], 2222: R1 = qlc:e(H2), 2223: R2 = qlc:e(H2, {max_list_size, 0}), % on temporary file 2224: ets:delete(E), 2225: true = lists:member(R1, L2s), 2226: true = R1 =:= R2">>, 2227: 2228: <<"E = ets:new(t, [duplicate_bag]), 2229: true = ets:insert(E, [{2},{1},{2}]), 2230: H1 = qlc:q([{X,Y} || 2231: Y <- [a,b], 2232: {X} <- ets:table(E)], 2233: [{cache,list}]), 2234: H2 = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]), 2235: L1s = [[{X,Y} || Y <- [a,b], X <- Xs] || Xs <- [[1,2,2], [2,2,1]]], 2236: L2s = [[{X,Y,Z} || X <- [3,4], {Y,Z} <- L1] || L1 <- L1s], 2237: R1 = qlc:e(H2), 2238: R2 = qlc:e(H2, {max_list_size, 0}), % on temporary file 2239: ets:delete(E), 2240: true = lists:member(R1, L2s), 2241: true = R1 =:= R2">>, 2242: 2243: <<"Q1 = qlc:q([{X,Y} || 2244: Y <- [a,b], 2245: {X,_} <- qlc_SUITE:table_error([{a,1}], 2, err)], 2246: [{cache,list}, unique]), 2247: Q = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- Q1]), 2248: {qlc,_,[{generate,_,{list,[3,4]}}, 2249: {generate,_,{qlc,_, 2250: [{generate,_,{list,[a,b]}}, 2251: {generate,_,{table,_}}], 2252: [{cache,list},{unique,true}]}}], 2253: []} = i(Q), 2254: err = qlc:e(Q,{max_list_size,0})">>, 2255: 2256: begin 2257: Privdir = ?privdir, 2258: [<<" 2259: E = ets:new(t, [duplicate_bag]), 2260: N = 17000, 2261: true = ets:insert(E, [{X,X} || X <- lists:seq(1, N)]), 2262: N = ets:info(E, size), 2263: RF = fun(GOpts) -> 2264: F = fun(CU) -> 2265: H1 = qlc:q([{X,Y} || 2266: Y <- [a,b], 2267: {X,_} <- ets:table(E)], 2268: CU), 2269: qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]) 2270: end, 2271: Q1 = F([{cache,list}, unique]), 2272: _ = qlc:info(Q1, GOpts), 2273: R1 = qlc:e(Q1, GOpts), 2274: Q2 = F([unique]), 2275: R2 = qlc:e(Q2, GOpts), 2276: true = lists:sort(R1) =:= lists:sort(R2) 2277: end, 2278: GOpts = [{tmpdir,\"">>,Privdir,<<"\"}], 2279: RF([{max_list_size, 1 bsl 35} | GOpts]), 2280: RF(GOpts), 2281: RF([{max_list_size, 40000} | GOpts]), 2282: true = ets:insert(E, [{X,X} || X <- lists:seq(1, N)]), 2283: true = N+N =:= ets:info(E, size), 2284: RF([{max_list_size, 1 bsl 30} | GOpts]), 2285: RF(GOpts), 2286: RF([{max_list_size, 40000} | GOpts]), 2287: ets:delete(E)">>] 2288: end, 2289: 2290: <<"%% Temporary file employed. 2291: etsc(fun(E) -> 2292: Q0 = qlc:q([X || X <- ets:table(E), 2293: begin element(1, X) > 5 end], 2294: {cache,list}), 2295: Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5), 2296: Y <- Q0]), 2297: R = [{X,Y} || X <- lists:seq(1, 5), 2298: Y <- lists:seq(6, 10)], 2299: R = lists:sort(qlc:e(Q, {max_list_size, 100*1024})) 2300: end, [{keypos,1}], [{I,a,lists:duplicate(100000,1)} || 2301: I <- lists:seq(1, 10)])">>, 2302: 2303: <<"%% Temporary file employed. The file is removed after error. 2304: L = [{I,a,lists:duplicate(100000,1)} || I <- lists:seq(1, 10)], 2305: Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err), 2306: begin element(1, X) > 5 end], 2307: {cache,list}), 2308: Q = qlc:q([{X, element(1,Y)} || 2309: X <- lists:seq(1, 5), 2310: Y <- Q0]), 2311: err = qlc:e(Q)">>, 2312: 2313: <<"%% Temporary file employed. The file is removed after error. 2314: L = [{I,a,lists:duplicate(100000,1)} || I <- lists:seq(1, 10)], 2315: Q0 = qlc:q([X || X <- qlc_SUITE:table(L, 1, []), 2316: begin element(1, X) > 5 end], 2317: {cache,list}), 2318: Q = qlc:q([{X, element(1,Y)} || 2319: X <- lists:seq(1, 5), 2320: Y <- Q0]), 2321: {error, _, {file_error,_,_}} = qlc:e(Q, {tmpdir, \"/a/b/c\"})">>, 2322: 2323: <<"Q = qlc:q([X || X <- [1,2]]), 2324: {'EXIT', {badarg, _}} = (catch qlc:e(Q, {max_list_size, -1}))">>, 2325: 2326: <<"Q = qlc:q([X || X <- [1,2]]), 2327: {'EXIT', {badarg, _}} = (catch qlc:e(Q, {max_list_size, foo}))">> 2328: 2329: ], 2330: ?line run(Config, Ts), 2331: ok. 2332: 2333: filter(doc) -> 2334: "Filters and match specs."; 2335: filter(suite) -> []; 2336: filter(Config) when is_list(Config) -> 2337: Ts = [ 2338: <<"L = [1,2,3,4,5], 2339: QH1 = qlc:q([X || X <- L, X > 1, X < 4]), 2340: [2,3] = qlc:e(QH1), 2341: {list,{list,L},_MS} = i(QH1) 2342: ">>, 2343: 2344: <<"L = [1,2,3,4,5], 2345: QH2 = qlc:q([X || X <- L, X > 1, X < 4, X > 2]), 2346: [3] = qlc:e(QH2), 2347: {list,{list,L},_MS} = i(QH2) 2348: ">>, 2349: 2350: %% "X > 1" is skipped since the matchspec does the job 2351: <<"QH3 = qlc:q([X || X <- [1,2,3,4,5], X > 1, begin X < 4 end]), 2352: [2,3] = qlc:e(QH3), 2353: {qlc,_,[{generate,_,{list,{list,[1,2,3,4,5]},_MS}},_],[]} = i(QH3) 2354: ">>, 2355: 2356: <<"QH4 = qlc:q([{X,Y} || X <- [1,2], Y <- [1,2]]), 2357: [{1,1},{1,2},{2,1},{2,2}] = qlc:e(QH4), 2358: {qlc,_,[{generate,_,{list,[1,2]}},{generate,_,{list,[1,2]}}],[]} = 2359: i(QH4)">>, 2360: 2361: %% "X > 1" is skipped since the matchspec does the job 2362: <<"QH5 = qlc:q([{X,Y} || X <- [1,2], X > 1, Y <- [1,2]]), 2363: [{2,1},{2,2}] = qlc:e(QH5), 2364: {qlc,_,[{generate,_,{list,{list,[1,2]},_MS}}, 2365: {generate,_,{list,[1,2]}}],[]} = 2366: i(QH5)">>, 2367: 2368: <<"%% Binaries are not handled at all when trying to find lookup values 2369: etsc(fun(E) -> 2370: A = 2, 2371: Q = qlc:q([X || {X} <- ets:table(E), <<A>> =:= <<X>>]), 2372: [2] = lists:sort(qlc:e(Q)), 2373: false = lookup_keys(Q) 2374: end, [{1},{2},{3}])">>, 2375: 2376: <<"etsc(fun(E) -> 2377: Q = qlc:q([X || {X,_} <- ets:table(E), 2378: qlc:e(qlc:q([Y || {Y,_} <- ets:table(E), 2379: Y > X])) == []]), 2380: [3] = qlc:e(Q) 2381: end, [{1,a},{2,b},{3,c}])">>, 2382: 2383: <<"Q = qlc:q([X || {X} <- [], (false or (X/0 > 3))]), 2384: \"[]\" = qlc:info(Q), 2385: [] = qlc:e(Q)">>, 2386: 2387: <<"%% match spec 2388: [] = qlc:e(qlc:q([X || {X} <- [{1},{2}], 2389: (false orelse (X/0 > 3))])), 2390: %% generated code 2391: {'EXIT', {badarith, _}} = 2392: (catch qlc:e(qlc:q([X || {X} <- [{1}], 2393: begin (false orelse (X/0 > 3)) end])))">>, 2394: 2395: <<"%% Partial evaluation in filter. 2396: etsc(fun(E) -> 2397: QH = qlc:q([{X+1,Y} || {X,Y} <- ets:table(E), 2398: X =:= 1-1+1+(+1)]), 2399: [{3,2}] = qlc:e(QH), 2400: [2] = lookup_keys(QH) 2401: end, [{1,1},{2,2},{3,3}])">>, 2402: 2403: <<"%% =/2 in filters must not be recognized when 'badmatch' is 2404: %% possible. 2405: etsc(fun(E) -> 2406: QH = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 2407: ((Y = X) =:= 3)]), 2408: {'EXIT', {{badmatch,4},_}} = (catch qlc:e(QH)), 2409: false = lookup_keys(QH) 2410: end, [{3,3},{4,true}])">>, 2411: 2412: <<"%% One more of the same kind. 2413: etsc(fun(E) -> 2414: QH = qlc:q([{X,Y} || {X,_} <- ets:table(E), 2415: (Y=X) =:= (Y=1+1)]), 2416: {'EXIT', {{badmatch,2},_}} = (catch qlc:e(QH)), 2417: false = lookup_keys(QH) 2418: end, [{1,1},{2,2},{3,3}])">>, 2419: 2420: <<"%% OTP-5195. Used to return a value, but badarith is correct. 2421: etsc(fun(E) -> 2422: QH = qlc:q([X || {X,_} <- ets:table(E), 2423: (X =:= 1) and 2424: if X =:= 1 -> true; 2425: true -> X/0 2426: end]), 2427: {'EXIT',{badarith,_}} = (catch qlc:e(QH)), 2428: false = lookup_keys(QH) 2429: end, [{1,1},{2,2},{3,3}])">>, 2430: 2431: <<"fun(Z) -> 2432: Q = qlc:q([X || Z < 2, X <- [1,2,3]]), 2433: [] = qlc:e(Q) 2434: end(3)">>, 2435: 2436: <<"H = qlc:q([{P1,A,P2,B,P3,C} || 2437: P1={A,_} <- [{1,a},{2,b}], 2438: {_,B}=P2 <- [{1,a},{2,b}], 2439: C=P3 <- [1], 2440: {[X,_],{_,X}} <- [{[1,2],{3,1}}, {[a,b],{3,4}}], 2441: A > 0, 2442: B =/= c, 2443: C > 0]), 2444: L = [{{1,a},1,{1,a},a,1,1}, {{1,a},1,{2,b},b,1,1}, 2445: {{2,b},2,{1,a},a,1,1}, {{2,b},2,{2,b},b,1,1}], 2446: L = qlc:e(H)">>, 2447: 2448: <<"H = qlc:q([{X,Y} || 2449: X = _ <- [1,2,3], 2450: _ = Y <- [a,b,c], 2451: _ = _ <- [foo], 2452: X > 1, 2453: Y =/= a]), 2454: [{2,b},{2,c},{3,b},{3,c}] = qlc:e(H)">> 2455: 2456: ], 2457: ?line run(Config, Ts), 2458: ok. 2459: 2460: info(doc) -> 2461: "info/2."; 2462: info(suite) -> []; 2463: info(Config) when is_list(Config) -> 2464: Ts = [ 2465: <<"{list, [1,2]} = i(qlc:q([X || X <- [1,2]])), 2466: {append,[{list, [1,2]}, {list, [3,4]}]} = 2467: i(qlc:append([1,2],[3,4])), 2468: {sort,{list, [1,2]},[]} = i(qlc:sort([1,2])), 2469: E = ets:new(foo, []), 2470: ets:insert(E, [{1},{2}]), 2471: {table, _} = i(ets:table(E)), 2472: true = ets:delete(E), 2473: {list, [1,2]} = i([1,2]), 2474: {append, [{list, [1,2]}, {list, [3,4]}]} = 2475: i(qlc:q([X || X <- qlc:append([1,2],[3,4])])), 2476: 2477: H0 = qlc:q([X || X <- throw({throw,t})]), 2478: {throw,t} = (catch {any_term,qlc:info(H0)}), 2479: {'EXIT', {badarg, _}} = 2480: (catch qlc:info(foobar)), 2481: {'EXIT', {badarg, _}} = 2482: (catch qlc:info(qlc:q([X || X <- [1,2]]), badarg))">>, 2483: 2484: <<"{'EXIT', {badarg, _}} = 2485: (catch qlc:info([X || {X} <- []], {n_elements, 0})), 2486: L = lists:seq(1, 1000), 2487: \"[1,2,3,4,5,6,7,8,9,10|'...']\" = qlc:info(L, {n_elements, 10}), 2488: {cons,1,{integer,1,1},{atom,1,'...'}} = 2489: qlc:info(L, [{n_elements, 1},{format,abstract_code}]), 2490: Q = qlc:q([{X} || X <- [a,b,c,d,e,f]]), 2491: {call,_,_,[{cons,_,{atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c}, 2492: {atom,_,'...'}}}}, 2493: {call,_,_,_}]} = 2494: qlc:info(Q, [{n_elements, 3},{format,abstract_code}]), 2495: \"ets:match_spec_run([a,b,c,d,e,f],\n\" 2496: \" ets:match_spec_compile([{'$1',[true],\" 2497: \"[{{'$1'}}]}]))\" = 2498: qlc:info(Q, [{n_elements, infinity}])">>, 2499: 2500: <<"Q1 = qlc:q([{X} || X <- qlc:q([X || X <- [1,2]])]), 2501: {qlc, _, [{generate, _, {list, [1,2]}}],[]} = i(Q1), 2502: Q2 = qlc:q([X || X <- qlc:q([{X} || X <- [1,2]])]), 2503: {list,{list,[1,2]},_} = i(Q2), 2504: [{1},{2}] = qlc:eval(Q2), 2505: Q3 = qlc:q([{X,Y} || X <- qlc:q([X || X <- [a,b]]), 2506: Y <- qlc:q([Z || Z <- [a,b]])]), 2507: {qlc, _, [{generate, _, {list, [a,b]}}, 2508: {generate, _, {list, [a,b]}}], []} = i(Q3), 2509: Q4 = qlc:q([X || X <- [a]]), 2510: {list, [a]} = i(Q4), 2511: Q5 = qlc:q([X || X <- qlc:q([Y || Y <- [a,b]], unique)]), 2512: {qlc, _, [{generate, _, {list, [a,b]}}], [{unique,true}]} = 2513: i(Q5)">>, 2514: 2515: <<"H = qlc:q([X || X <- qlc:append([qlc:q([X || X <- [1,2]]),[1,2]])]), 2516: {append, [{list, [1,2]},{list, [1,2]}]} = i(H), 2517: [1,2,1,2] = qlc:e(H)">>, 2518: 2519: <<"H = qlc:q([{X} || X <- [], X > 1]), 2520: {list, []} = i(H), 2521: [] = qlc:e(H)">>, 2522: 2523: <<"H1 = qlc:q([{X} || X <- [], X > 1]), 2524: H = qlc:q([{X} || X <- H1, X < 10]), 2525: {list, []} = i(H), 2526: [] = qlc:e(H)">>, 2527: 2528: <<"L = [1,2,3], 2529: QH1 = qlc:q([{X} || X <- L, X > 1]), 2530: QH2 = qlc:q([{X} || X <- QH1]), 2531: [{{2}},{{3}}] = qlc:e(QH2), 2532: {list,{list,{list,L},_},_} = i(QH2)">>, 2533: 2534: <<"H = qlc:q([X || X <- qlc:q([Y || Y <- qlc:q([Z || Z <-[1,2,1]])])]), 2535: {list, [1,2,1]} = i(H), 2536: [1,2,1] = qlc:eval(H)">>, 2537: 2538: <<"%% Used to replace empty ETS tables with [], but that won't work. 2539: E = ets:new(apa,[]), 2540: QH1 = qlc:q([{X} || X <- ets:table(E), X > 1]), 2541: QH2 = qlc:q([{X} || X <- QH1], cache), 2542: [] = qlc:e(QH2), 2543: {qlc,_,[{generate,_,{table,{ets,table,_}}}],[]} = i(QH2), 2544: ets:delete(E)">>, 2545: 2546: <<"Q1 = qlc:q([W || W <- [a,b]]), 2547: Q2 = qlc:q([Z || Z <- qlc:sort([1,2,300])], unique), 2548: Q3 = qlc:q([{X,Y} || X <- qlc:keysort([2], [{1,a}]), 2549: Y <- qlc:append([Q1, Q2]), 2550: X > Y]), 2551: {qlc, T1, 2552: [{generate, P1, {list, [{1,a}]}}, 2553: {generate, P2, {append, [{list, [a,b]}, 2554: {qlc, T2, [{generate, P3, 2555: {sort, {list,[1,2,300]},[]}}], 2556: [{cache,ets},{unique,true}]}]}},F], 2557: []} = i(Q3, cache_all), 2558: {tuple, _, [{var,_,'X'}, {var,_,'Y'}]} = binary_to_term(T1), 2559: {var, _, 'X'} = binary_to_term(P1), 2560: {var, _, 'Y'} = binary_to_term(P2), 2561: {var, _, 'Z'} = binary_to_term(P3), 2562: {var, _, 'Z'} = binary_to_term(T2), 2563: {op, _, '>', {var, _, 'X'}, {var, _, 'Y'}} = binary_to_term(F), 2564: true = binary_to_list(<< 2565: \"beginV1=qlc:q([Z||Z<-qlc:sort([1,2,300],[])],[{unique,true}]),\" 2566: \"qlc:q([{X,Y}||X<-[{1,a}],Y<-qlc:append([[a,b],V1]),X>Y])end\" 2567: >>) == format_info(Q3, true)">>, 2568: 2569: <<"Q1 = qlc:q([{X} || X <- qlc:q([X || X <- [a,b]])]), 2570: {qlc, _, [{generate, _, {list, [a,b]}}], []} = i(Q1), 2571: Q2 = qlc:q([X || X <- qlc:q([{X} || X <- [a,b]])]), 2572: {list,{list,[a,b]},_} = i(Q2), 2573: [{a},{b}] = qlc:eval(Q2)">>, 2574: 2575: <<"Q = qlc:keysort(2, [{1,a,b},{2,b,c},{3,4,c}]), 2576: {keysort,{list,[{1,a,b},{2,b,c},{3,4,c}]},2,[]} = i(Q), 2577: true = binary_to_list(<< 2578: \"qlc:keysort(2,[{1,a,b},{2,b,c},{3,4,c}],[])\">>) 2579: == format_info(Q, true), 2580: [{3,4,c},{1,a,b},{2,b,c}] = qlc:e(Q)">>, 2581: 2582: <<"E = ets:new(foo, []), 2583: ets:insert(E, [{1},{2}]), 2584: Q = qlc_SUITE:default_table(E), 2585: {table,{'$MOD','$FUN',[]}} = i(Q), 2586: true = binary_to_list(<<\"'$MOD':'$FUN'()\">>) 2587: == format_info(Q, true), 2588: true = ets:delete(E)">>, 2589: 2590: <<"\"[]\" = qlc:info([], flat), 2591: \"[]\" = qlc:info([]), 2592: \"[]\" = qlc:info([], {flat, true})">>, 2593: 2594: <<"H = qlc:q([{X} || X <- [a,b]]), 2595: \"ets:match_spec_run([a,b],ets:match_spec_compile(\" ++ _ = 2596: format_info(H, true), 2597: \"ets:match_spec_run([a,b],ets:match_spec_compile(\" ++ _ = 2598: format_info(H, false)">>, 2599: 2600: <<"H = qlc:q([{X} || X <- [a,b], begin true end]), 2601: true = binary_to_list(<<\"qlc:q([{X}||X<-[a,b],begintrueend])\">>) 2602: == format_info(H, true), 2603: true = binary_to_list(<<\"qlc:q([{X}||X<-[a,b],begintrueend])\">>) 2604: == format_info(H, false)">>, 2605: 2606: <<"H = qlc:q([A || {A} <- [{1},{2}], (A =:= 2) andalso true]), 2607: {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} = 2608: qlc:info(H, {format,abstract_code})">>, 2609: 2610: <<"H = qlc:q([{X} || X <- qlc:q([{X} || X <- [a,b], begin true end], 2611: unique), 2612: begin true end]), 2613: true = binary_to_list(<< 2614: \"beginV1=qlc:q([{X}||X<-[a,b],begintrueend],[{unique,true}]),\" 2615: \"qlc:q([{X}||X<-V1,begintrueend])end\">>) == 2616: format_info(H, true), 2617: true = binary_to_list(<< 2618: \"qlc:q([{X}||X<-qlc:q([{X}||X<-[a,b],begintrueend],\" 2619: \"[{unique,true}]),begintrueend])\">>) == format_info(H, false)">>, 2620: 2621: <<"H0 = qlc:q([{V3} || V3 <- qlc:q([{V1} || V1 <- [a,b], 2622: begin true end], unique), 2623: begin true end]), 2624: H = qlc:sort(H0), 2625: true = binary_to_list(<< 2626: \"qlc:sort(qlc:q([{V3}||V3<-qlc:q([{V1}||\" 2627: \"V1<-[a,b],begintrueend],[{unique,true}]),begintrueend]),[])\">>) 2628: == format_info(H, false), 2629: true = binary_to_list(<< 2630: \"beginV2=qlc:q([{V1}||V1<-[a,b],begintrueend],[{unique,true}]),\" 2631: \"V4=qlc:q([{V3}||V3<-V2,begintrueend]),qlc:sort(V4,[])end\">>) 2632: == format_info(H, true)">>, 2633: 2634: <<"H0 = qlc:q([X || X <- [true], begin true end]), 2635: H1 = qlc:q([{X} || X <- [a,b], begin true end], 2636: [{unique,begin [T] = qlc:e(H0), T end}]), 2637: {call,_,{remote,_,{atom,_,qlc},{atom,_,q}}, 2638: [{lc,_,{tuple,_,[{var,_,'X'}]}, 2639: [{generate,_,{var,_,'X'}, 2640: {cons,_,{atom,_,a},_}}, 2641: {block, _, [{atom, _, true}]}]}, 2642: {cons,_,_,_}]} = i(H1, {format, abstract_code})">>, 2643: 2644: <<"E = ets:new(apa, [duplicate_bag]), 2645: true = ets:insert(E, [{1,a},{2,b},{3,c},{4,d}]), 2646: QH = qlc:q([X || {X,_} <- ets:tab2list(E), X > 2], unique), 2647: {qlc, _, [{generate, _, {list, _, _MS}}], [{unique, true}]} = 2648: i(QH), 2649: [3,4] = lists:sort(qlc:e(QH)), 2650: ets:delete(E)">>, 2651: 2652: %% "Imported" variable. 2653: <<"F = fun(U) -> qlc:q([{X} || X <- [1,2,3,4,5,6], X > U]) end, 2654: QH = F(4), 2655: {call, _ , 2656: {remote, _, {atom, _, ets},{atom, _, match_spec_run}}, 2657: [{string, _, [1,2,3,4,5,6]}, 2658: {call, _, 2659: _compile, 2660: [{cons, _, 2661: {tuple, _, 2662: [{atom, _,'$1'}, 2663: {cons, _, 2664: {tuple, 2665: _, 2666: [{atom, _,'>'}, 2667: {atom, _,'$1'}, 2668: {tuple, 2669: _, 2670: [{atom, _,const}, 2671: {integer, _,4}]}]}, 2672: _}, 2673: {cons, _, _, _}]}, 2674: {nil,_}}]}]} = i(QH, {format, abstract_code}), 2675: [{5},{6}] = qlc:e(QH), 2676: [{4},{5},{6}] = qlc:e(F(3))">> 2677: 2678: ], 2679: ?line run(Config, Ts), 2680: ok. 2681: 2682: nested_info(doc) -> 2683: "Nested QLC expressions. QLC expressions in filter and template."; 2684: nested_info(suite) -> []; 2685: nested_info(Config) when is_list(Config) -> 2686: Ts = [ 2687: <<"L = [{1,a},{2,b},{3,c}], 2688: Q = qlc:q( 2689: [{X,R} || 2690: {X,_} <- qlc_SUITE:table(L, []), 2691: begin % X imported 2692: R = qlc:e(qlc:q([{X,Y} || {Y,_} 2693: <- qlc_SUITE:table(L, []), 2694: Y > X])), 2695: true 2696: end]), 2697: true = binary_to_list(<< 2698: \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\" 2699: \"beginR=qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),Y>X]))\" 2700: \",trueend])\">>) == format_info(Q, true), 2701: [{1,[{1,2},{1,3}]},{2,[{2,3}]},{3,[]}] = qlc:e(Q)">>, 2702: 2703: <<"L = [{1,a},{2,b},{3,c}], 2704: Q = qlc:q( % X imported 2705: [{X,qlc:e(qlc:q([{X,Y} || {Y,_} <- qlc_SUITE:table(L, []), 2706: Y > X]))} || 2707: {X,_} <- qlc_SUITE:table(L, [])]), 2708: true = binary_to_list(<< 2709: \"qlc:q([{X,qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\" 2710: \"Y>X]))}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}])])\">>) 2711: == format_info(Q, true), 2712: [{1,[{1,2},{1,3}]},{2,[{2,3}]},{3,[]}] = qlc:e(Q)">>, 2713: 2714: <<"L = [{1,a},{2,b},{3,c}], 2715: Q = qlc:q( 2716: [{X,R} || 2717: {X,_} <- qlc_SUITE:table(L, []), 2718: begin % X imported 2719: R = qlc:e(qlc:q([{X,Y} || {Y,_} 2720: <- qlc_SUITE:table(L, []), 2721: Y =:= X])), 2722: true 2723: end]), 2724: true = binary_to_list(<< 2725: \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\" 2726: \"beginR=qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\" 2727: \"Y=:=X])),trueend])\">>) == format_info(Q, true), 2728: [{1,[{1,1}]},{2,[{2,2}]},{3,[{3,3}]}] = qlc:e(Q)">>, 2729: 2730: <<"L = [{1,a},{2,b},{3,c}], 2731: Q = qlc:q( 2732: [{X, % X imported 2733: qlc:e(qlc:q([{X,Y} || {Y,_} <- qlc_SUITE:table(L, []), 2734: Y =:= X]))} || 2735: {X,_} <- qlc_SUITE:table(L, [])]), 2736: true = binary_to_list(<< 2737: \"qlc:q([{X,qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\" 2738: \"Y=:=X]))}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}])])\">>) 2739: == format_info(Q, true), 2740: [{1,[{1,1}]},{2,[{2,2}]},{3,[{3,3}]}] = qlc:e(Q)">>, 2741: 2742: <<"L = [{1,a},{2,b},{3,c}], 2743: Q = qlc:q( 2744: [{X,R} || 2745: {X,_} <- qlc_SUITE:table(L, []), 2746: begin 2747: R = qlc:e(qlc:q([Y || Y <- [X]])), 2748: true 2749: end]), 2750: true = binary_to_list(<< 2751: \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\" 2752: \"beginR=qlc:e(qlc:q([Y||Y<-[X]])),trueend])\">>) 2753: == format_info(Q, true), 2754: [{1,[1]},{2,[2]},{3,[3]}] = qlc:e(Q)">>, 2755: 2756: <<"L = [{1,a},{2,b},{3,c}], 2757: Q = qlc:q( 2758: [{X,qlc:e(qlc:q([Y || Y <- [X]]))} || 2759: {X,_} <- qlc_SUITE:table(L, [])]), 2760: true = binary_to_list(<< 2761: \"qlc:q([{X,qlc:e(qlc:q([Y||Y<-[X]]))}||{X,_}<-qlc_SUITE:\" 2762: \"the_list([{1,a},{2,b},{3,c}])])\">>) == format_info(Q, true), 2763: [{1,[1]},{2,[2]},{3,[3]}] = qlc:e(Q)">>, 2764: 2765: <<"L = [{1,a},{2,b}], 2766: Q = qlc:q( 2767: [{X,Y} || 2768: {X,_} <- qlc_SUITE:table(L, []), 2769: {Y,_} <- qlc:q( 2770: [{Z,V} || 2771: {Z,_} <- qlc_SUITE:table(L, []), 2772: {V} <- qlc:q( 2773: [{W} || W 2774: <- qlc_SUITE:table(L, [])]) 2775: ]) 2776: ]), 2777: true = binary_to_list(<< 2778: \"beginV1=qlc:q([{W}||W<-qlc_SUITE:the_list([{1,a},{2,b}])]),\" 2779: \"V2=qlc:q([{Z,V}||{Z,_}<-qlc_SUITE:the_list([{1,a},{2,b}]),\" 2780: \"{V}<-V1]),qlc:q([{X,Y}||{X,_}<-qlc_SUITE:the_list([{1,a},\" 2781: \"{2,b}]),{Y,_}<-V2])end\">>) == format_info(Q, true), 2782: [{1,1},{1,1},{1,2},{1,2},{2,1},{2,1},{2,2},{2,2}] = qlc:e(Q)">> 2783: 2784: ], 2785: ?line run(Config, Ts), 2786: ok. 2787: 2788: 2789: lookup1(doc) -> 2790: "Lookup keys. Mostly test of patterns."; 2791: lookup1(suite) -> []; 2792: lookup1(Config) when is_list(Config) -> 2793: Ts = [ 2794: <<"etsc(fun(E) -> 2795: Q = qlc:q([A || {A=3} <- ets:table(E)]), 2796: [3] = qlc:eval(Q), 2797: [3] = lookup_keys(Q) 2798: end, [{1},{2},{3},{4}])">>, 2799: 2800: <<"etsc(fun(E) -> 2801: Q = qlc:q([A || {A=3} <- ets:table(E)],{max_lookup,0}), 2802: [3] = qlc:eval(Q), 2803: false = lookup_keys(Q) 2804: end, [{1},{2},{3},{4}])">>, 2805: 2806: <<"%% The lookup and max_lookup options interact. 2807: etsc(fun(E) -> 2808: Q = qlc:q([X || {X} <- ets:table(E), 2809: (X =:= 1) or (X =:= 2)], 2810: [{lookup,true},{max_lookup,1}]), 2811: {'EXIT', {no_lookup_to_carry_out, _}} = (catch qlc:e(Q)) 2812: end, [{1},{2}])">>, 2813: 2814: <<"etsc(fun(E) -> 2815: Q = qlc:q([{A,B,C,D} || {A,B}={C,D} <- ets:table(E)]), 2816: [{1,2,1,2},{3,4,3,4}] = lists:sort(qlc:eval(Q)), 2817: false = lookup_keys(Q) 2818: end, [{1,2},{3,4}])">>, 2819: 2820: <<"etsc(fun(E) -> 2821: Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E)]), 2822: [{1,1,1},{2,2,2}] = lists:sort(qlc:eval(Q)), 2823: false = lookup_keys(Q) 2824: end, [{1,2},{2,2},{1,1}])">>, 2825: 2826: <<"etsc(fun(E) -> 2827: Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E), 2828: (D =:= 2) or (B =:= 1)], 2829: {max_lookup,infinity}), 2830: [{1,1,1},{2,2,2}] = qlc:eval(Q), 2831: [1,2] = lookup_keys(Q) 2832: end, [{1,2},{2,2},{1,1}])">>, 2833: 2834: <<"etsc(fun(E) -> 2835: Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E), 2836: (D =:= 2) xor (B =:= 1)]), 2837: [{1,1,1},{2,2,2}] = qlc:eval(Q), 2838: [1,2] = lookup_keys(Q) 2839: end, [{1,2},{2,2},{1,1}])">>, 2840: 2841: <<"etsc(fun(E) -> 2842: Q = qlc:q([3 || {{[3,4],apa}} <- ets:table(E)]), 2843: [3] = qlc:e(Q), 2844: [{[3,4],apa}] = lookup_keys(Q) 2845: end, [{{[4,3],foo}},{{[3,4],apa}}])">>, 2846: 2847: <<"etsc(fun(E) -> 2848: Q = qlc:q([3 || {3} <- ets:table(E)]), 2849: [3] = qlc:e(Q), 2850: [3] = lookup_keys(Q) 2851: end, [{2},{3},{4}])">>, 2852: 2853: <<"etsc(fun(E) -> 2854: Q = qlc:q([{X,Y,Z} || {{X,_},Y,Y={_,Z},X,Y} <- ets:table(E)]), 2855: [] = qlc:e(Q), 2856: false = lookup_keys(Q) 2857: end, [{{1,1},1,{1,1},1,1}])">>, 2858: 2859: <<"etsc(fun(E) -> 2860: Q = qlc:q([X || {X,X=2} <- ets:table(E)]), 2861: [2] = qlc:e(Q), 2862: [2] = lookup_keys(Q) 2863: end, [{2,2},{3,3}])">>, 2864: 2865: <<"etsc(fun(E) -> 2866: Q = qlc:q([X || {{_,3}={4,_}=X} <- ets:table(E)]), 2867: [{4,3}] = qlc:e(Q), 2868: [{4,3}] = lookup_keys(Q) 2869: end, [{{2,3}},{{4,3}}])">>, 2870: 2871: <<"U = 17.0, 2872: etsc(fun(E) -> 2873: Q = qlc:q([X || {_=X=_} <- ets:table(E)]), 2874: [U] = qlc:e(Q), 2875: false = lookup_keys(Q) 2876: end, [{U},{U+U,U}])">>, 2877: 2878: <<"etsc(fun(E) -> 2879: Q = qlc:q([{X,Y,Z,W} || {X=Y}=Z={V=W} <- ets:table(E), 2880: V == {h,g}]), 2881: [{{h,g},{h,g},{{h,g}},{h,g}}] = qlc:e(Q), 2882: [{h,g}] = lookup_keys(Q) 2883: end, [{h,g},{{h,g}}])">>, 2884: 2885: <<"etsc(fun(E) -> 2886: Q = qlc:q([{C,Y,Z,X} || {{X=Y}=Z}={{A=B}=C} <- ets:table(E), 2887: A == a, B =/= c]), 2888: [{{a},a,{a},a}] = qlc:e(Q), 2889: [{a}] = lookup_keys(Q) 2890: end, [{{1}},{{a}}])">>, 2891: 2892: <<"etsc(fun(E) -> 2893: Q = qlc:q([{A,D,Y,X} || 2894: {{A={B=C}},{D={C}}} = {X,Y} <- ets:table(E), 2895: [] == B]), 2896: [{{[]},{[]},{{[]}},{{[]}}}] = qlc:e(Q), 2897: [{{[]}}] = lookup_keys(Q) 2898: end, [{{{[]}},{{[]}}}])">>, 2899: 2900: {cres, 2901: <<"etsc(fun(E) -> 2902: Q = qlc:q([X || {X}=X <- ets:table(E)]), 2903: [] = qlc:e(Q), 2904: false = lookup_keys(Q) 2905: end, [{1},{a}])">>, 2906: {warnings,[{{2,37},qlc,nomatch_pattern}]}}, 2907: 2908: <<"etsc(fun(E) -> 2909: Q = qlc:q([X || {X=X,Y=Y}={Y=Y,X=X} <- ets:table(E), 2910: {} == X]), 2911: [{}] = qlc:e(Q), 2912: [{}] = lookup_keys(Q) 2913: end, [{{},{}},{[],[]}])">>, 2914: 2915: <<"etsc(fun(E) -> 2916: Q = qlc:q([X || {3+4=7,X} <- ets:table(E), 2917: X =:= 3+997]), 2918: [1000] = qlc:e(Q), 2919: [7] = lookup_keys(Q) 2920: end, [{7,1000},{8,1000}])">>, 2921: 2922: <<"etsc(fun(E) -> 2923: Q = qlc:q([{X, Y} || [X]=[Y] <- ets:table(E)]), 2924: [] = qlc:eval(Q), 2925: false = lookup_keys(Q) 2926: end, [{a}])">>, 2927: 2928: {cres, 2929: <<"etsc(fun(E) -> 2930: Q = qlc:q([X || X={1,2,3,X,5} <- ets:table(E)]), 2931: [] = qlc:e(Q), 2932: false = lookup_keys(Q) 2933: end, [{a},{b}])">>, 2934: {warnings,[{{2,35},qlc,nomatch_pattern}]}}, 2935: 2936: {cres, 2937: <<"etsc(fun(E) -> 2938: Q = qlc:q([X || X=[1,2,3,X,5] <- ets:table(E)]), 2939: [] = qlc:e(Q), 2940: false = lookup_keys(Q) 2941: end, [{a},{b}])">>, 2942: {warnings,[{{2,35},qlc,nomatch_pattern}]}}, 2943: 2944: <<"etsc(fun(E) -> 2945: Q = qlc:q([X || X = <<X>> <- ets:table(E)]), 2946: [] = qlc:e(Q), 2947: false = lookup_keys(Q) 2948: end, [{a},{b}])">>, 2949: 2950: <<"Tre = 3.0, 2951: etsc(fun(E) -> 2952: Q = qlc:q([{A,B} || {A,B}={{a,C},{a,C}} <- ets:table(E), 2953: C =:= Tre]), 2954: [] = qlc:e(Q), 2955: [{a,Tre}] = lookup_keys(Q) 2956: end, [{a,b}])">>, 2957: 2958: <<"A = 3, 2959: etsc(fun(E) -> 2960: Q = qlc:q([X || X <- ets:table(E), A =:= element(1, X)]), 2961: [{3,3}] = qlc:e(Q), 2962: [3] = lookup_keys(Q) 2963: end, [{1,a},{3,3}])">>, 2964: 2965: <<"A = 3, 2966: etsc(fun(E) -> 2967: Q = qlc:q([X || X <- ets:table(E), A =:= erlang:element(1, X)]), 2968: [{3,3}] = qlc:e(Q), 2969: [3] = lookup_keys(Q) 2970: end, [{1,a},{3,3}])">>, 2971: 2972: <<"etsc(fun(E) -> 2973: A = 3, 2974: Q = qlc:q([X || X <- ets:table(E), 2975: A == element(1,X), 2976: element(1,X) =:= a]), 2977: [] = qlc:e(Q), 2978: [a] = lookup_keys(Q) 2979: end, [{a},{b},{c}])">>, 2980: 2981: {cres, 2982: <<"etsc(fun(E) -> 2983: Q = qlc:q([X || {X = {[a,Z]}, 2984: Z = [foo, {[Y]}], 2985: Y = {{foo,[X]}}} <- ets:table(E)]), 2986: [] = qlc:e(Q), 2987: false = lookup_keys(Q) 2988: end, [{a,b,c},{d,e,f}])">>, 2989: {warnings,[{{2,34},qlc,nomatch_pattern}]}} 2990: 2991: ], 2992: ?line run(Config, Ts), 2993: ok. 2994: 2995: lookup2(doc) -> 2996: "Lookup keys. Mostly test of filters."; 2997: lookup2(suite) -> []; 2998: lookup2(Config) when is_list(Config) -> 2999: Ts = [ 3000: <<"%% Only guards are inspected. No lookup. 3001: etsc(fun(E) -> 3002: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 3003: ((Y = X) =:= 3)]), 3004: {'EXIT', {{badmatch,4},_}} = (catch qlc:e(Q)) 3005: end, [{3,3},{4,true}])">>, 3006: 3007: <<"%% Only guards are inspected. No lookup. 3008: etsc(fun(E) -> 3009: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 3010: Y = (X =:= 3)]), 3011: {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q)) 3012: end, [{false,3},{true,3}])">>, 3013: 3014: <<"%% Only guards are inspected. No lookup. 3015: etsc(fun(E) -> 3016: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 3017: Y = (X =:= 3)]), 3018: {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q)) 3019: end, [{3,true},{4,true}])">>, 3020: 3021: <<"%% Only guards are inspected. No lookup. 3022: E1 = create_ets(1, 10), 3023: E2 = ets:new(join, []), 3024: true = ets:insert(E2, [{true,1},{false,2}]), 3025: Q = qlc:q([{X,Z} || {_,X} <- ets:table(E1), 3026: {Y,Z} <- ets:table(E2), 3027: Y = (X =:= 3)]), 3028: {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q)), 3029: ets:delete(E1), 3030: ets:delete(E2)">>, 3031: 3032: <<"etsc(fun(E) -> 3033: Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E), 3034: (A =:= 3) or (4 =:= D)]), 3035: [{3,3,3},{4,4,4}] = lists:sort(qlc:e(Q)), 3036: [3,4] = lookup_keys(Q) 3037: end, [{2,2},{3,3},{4,4}])">>, 3038: 3039: <<"etsc(fun(E) -> 3040: Q = qlc:q([X || {X,U} <- ets:table(E), X =:= U]), 3041: [1] = qlc:e(Q), 3042: false = lookup_keys(Q) 3043: end, [{1,1}])">>, 3044: 3045: {cres, 3046: <<"etsc(fun(E) -> 3047: Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 3048: {[X],4} =:= {[3],X}]), 3049: [] = qlc:e(Q), 3050: false = lookup_keys(Q) 3051: end, [{1}, {2}])">>, 3052: {warnings,[{{3,46},qlc,nomatch_filter}]}}, 3053: 3054: {cres, 3055: <<"etsc(fun(E) -> 3056: Q = qlc:q([X || {X} <- ets:table(E), 3057: X == 1, X =:= 2]), 3058: [] = qlc:e(Q), 3059: false = lookup_keys(Q) 3060: end, [{1}, {2}])">>, 3061: {warnings,[{{3,43},qlc,nomatch_filter}]}}, 3062: 3063: {cres, 3064: <<"etsc(fun(E) -> 3065: Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 3066: {[X,Y],4} =:= {[3,X],X}]), 3067: [] = qlc:e(Q), 3068: false = lookup_keys(Q) 3069: end, [{1}, {2}])">>, 3070: {warnings,[{{3,48},qlc,nomatch_filter}]}}, 3071: 3072: <<"etsc(fun(E) -> 3073: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 3074: ({X,3} =:= {Y,Y}) or (X =:= 4)]), 3075: [{3,3},{4,4}] = lists:sort(qlc:e(Q)), 3076: [3,4] = lookup_keys(Q) 3077: end, [{2,2},{3,3},{4,4},{5,5}])">>, 3078: 3079: {cres, 3080: <<"etsc(fun(E) -> 3081: Q = qlc:q([X || {X} <- ets:table(E), {[X]} =:= {[3,4]}]), 3082: [] = qlc:e(Q), 3083: false = lookup_keys(Q) 3084: end, [{[3]},{[3,4]}])">>, 3085: {warnings,[{{2,61},qlc,nomatch_filter}]}}, 3086: 3087: <<"etsc(fun(E) -> 3088: U = 18, 3089: Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), [X|a] =:= [3|U]]), 3090: [] = qlc:e(Q), 3091: [3] = lookup_keys(Q) 3092: end, [{2}, {3}])">>, 3093: 3094: <<"etsc(fun(E) -> 3095: U = 18, V = 19, 3096: Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 3097: [X|V] =:= [3|U+1]]), 3098: [{3,3}] = qlc:e(Q), 3099: [3] = lookup_keys(Q) 3100: end, [{2},{3}])">>, 3101: 3102: <<"%% Blocks are not handled. 3103: etsc(fun(E) -> 3104: Q = qlc:q([X || {X} <- ets:table(E), begin X == a end]), 3105: [a] = qlc:e(Q), 3106: false = lookup_keys(Q) 3107: end, [{a},{b}])">>, 3108: 3109: {cres, 3110: <<"etsc(fun(E) -> 3111: Q = qlc:q([X || {X} <- ets:table(E), 3112: (3 =:= X) or (X =:= 12), 3113: (8 =:= X) or (X =:= 10)]), 3114: [] = lists:sort(qlc:e(Q)), 3115: false = lookup_keys(Q) 3116: end, [{2},{3},{4},{8}])">>, 3117: {warnings,[{{4,44},qlc,nomatch_filter}]}}, 3118: 3119: {cres, 3120: <<"etsc(fun(E) -> 3121: Q = qlc:q([X || {X} <- ets:table(E), 3122: ((3 =:= X) or (X =:= 12)) 3123: and ((8 =:= X) or (X =:= 10))]), 3124: [] = lists:sort(qlc:e(Q)), 3125: false = lookup_keys(Q) 3126: end, [{2},{3},{4},{8}])">>, 3127: {warnings,[{{4,35},qlc,nomatch_filter}]}}, 3128: 3129: <<"F = fun(U) -> 3130: Q = qlc:q([X || {X} <- [a,b,c], 3131: X =:= if U -> true; true -> false end]), 3132: [] = qlc:eval(Q), 3133: false = lookup_keys(Q) 3134: end, 3135: F(apa)">>, 3136: 3137: {cres, 3138: <<"etsc(fun(E) -> 3139: Q = qlc:q([X || {X=1,X} <- ets:table(E), X =:= 2]), 3140: [] = qlc:e(Q), 3141: false = lookup_keys(Q) 3142: end, [{1,1},{2,1}])">>, 3143: {warnings,[{{2,61},qlc,nomatch_filter}]}}, 3144: 3145: <<"Two = 2.0, 3146: etsc(fun(E) -> 3147: Q = qlc:q([X || {X} <- ets:table(E), X =:= Two]), 3148: [Two] = qlc:e(Q), 3149: [Two] = lookup_keys(Q) 3150: end, [{2.0},{2}])">>, 3151: 3152: <<"etsc(fun(E) -> 3153: %% This float is equal (==) to an integer. Not a constant! 3154: Q = qlc:q([X || {X} <- ets:table(E), X == {a,b,c,[2.0]}]), 3155: [_,_] = qlc:e(Q), 3156: false = lookup_keys(Q) 3157: end, [{{a,b,c,[2]}},{{a,b,c,[2.0]}}])">>, 3158: 3159: <<"%% Must _not_ regard floats as constants. Check imported variables 3160: %% in runtime. 3161: etsc(fun(E) -> 3162: U = 3.0, 3163: QH = qlc:q([X || {X,_} <- ets:table(E), X =:= U]), 3164: [] = qlc:e(QH), 3165: [U] = lookup_keys(QH) 3166: end, [{1,a},{2,b},{3,c},{4,d}])">>, 3167: 3168: <<"etsc(fun(E) -> 3169: Q = qlc:q([X || {X} <- ets:table(E), 3170: length(X) =:= 1]), 3171: [[1]] = qlc:e(Q), 3172: false = lookup_keys(Q) 3173: end, [{[1]},{[2,3]}])">>, 3174: 3175: <<"etsc(fun(E) -> 3176: A=3, 3177: Q = qlc:q([X || {X,Y} <- ets:table(E), X =:= A, Y =:= 3]), 3178: [3] = qlc:e(Q), 3179: [3] = lookup_keys(Q) 3180: end, [{3,3},{4,3}])">>, 3181: 3182: <<"etsc(fun(E) -> 3183: A = 1, 3184: Q = qlc:q([X || {X} <- ets:table(E), 3185: X =:= 1, <<X>> =:= <<A>>]), 3186: [1] = qlc:e(Q), 3187: [1] = lookup_keys(Q) 3188: end, [{1},{2}])">>, 3189: 3190: <<"etsc(fun(E) -> 3191: Q = qlc:q([X || {X} <- ets:table(E), X == a]), 3192: [a] = qlc:e(Q), 3193: [a] = lookup_keys(Q) 3194: end, [{a},{b},{c}])">>, 3195: 3196: {cres, 3197: <<"etsc(fun(E) -> 3198: Q = qlc:q([X || {X}=Y <- ets:table(E), 3199: element(2, Y) == b, 3200: X =:= 1]), 3201: [] = qlc:e(Q), 3202: false = lookup_keys(Q) 3203: end, [{1,b},{2,3}])">>, 3204: {warnings,[{2,sys_core_fold,nomatch_guard}, 3205: {3,qlc,nomatch_filter}, 3206: {3,sys_core_fold,{eval_failure,badarg}}]}}, 3207: 3208: <<"etsc(fun(E) -> 3209: Q = qlc:q([X || {X} <- ets:table(E), element(1,{X}) =:= 1]), 3210: [1] = qlc:e(Q), 3211: [1] = lookup_keys(Q) 3212: end, [{1}, {2}])">>, 3213: 3214: <<"etsc(fun(E) -> 3215: Q = qlc:q([X || {X} <- ets:table(E), 1 =:= element(1,{X})]), 3216: [1] = qlc:e(Q), 3217: [1] = lookup_keys(Q) 3218: end, [{1}, {2}])">>, 3219: 3220: {cres, 3221: <<"etsc(fun(E) -> 3222: Q = qlc:q([X || {X} <- ets:table(E), 3223: X =:= {1}, 3224: element(1,X) =:= 2]), 3225: [] = qlc:e(Q), 3226: false = lookup_keys(Q) 3227: end, [{{1}},{{2}}])">>, 3228: {warnings,[{{4,47},qlc,nomatch_filter}]}}, 3229: 3230: {cres, 3231: <<"etsc(fun(E) -> 3232: Q = qlc:q([X || {X} <- ets:table(E), 3233: X =:= {1}, 3234: element(1,X) =:= element(1, {2})]), 3235: [] = qlc:e(Q), 3236: false = lookup_keys(Q) 3237: end, [{{1}},{{2}}])">>, 3238: {warnings,[{{4,47},qlc,nomatch_filter}]}}, 3239: 3240: <<"etsc(fun(E) -> 3241: Q = qlc:q([X || {X} <- ets:table(E), 3242: element(1,X) =:= 1, X =:= {1}]), 3243: [{1}] = qlc:e(Q), 3244: [{1}] = lookup_keys(Q) 3245: end, [{{1}},{{2}}])">>, 3246: 3247: <<"etsc(fun(E) -> 3248: Q = qlc:q([X || {X} <- ets:table(E), 3249: {{element(1,element(1,{{1}}))}} =:= {X}]), 3250: [{1}] = qlc:e(Q), 3251: [{1}] = lookup_keys(Q) 3252: end, [{{1}},{{2}}])">>, 3253: 3254: <<"etsc(fun(E) -> 3255: Q = qlc:q([X || X <- ets:table(E), 3256: {element(1,element(1, {{1}}))} =:= 3257: {element(1,X)}]), 3258: [{1}] = qlc:e(Q), 3259: [1] = lookup_keys(Q) 3260: end, [{1},{2}])">>, 3261: 3262: <<"etsc(fun(E) -> 3263: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3264: (X =:= 1) and (Y =:= 2) 3265: or (X =:= 3) and (Y =:= 4)]), 3266: [1,3] = lists:sort(qlc:e(Q)), 3267: [{1,2}, {3,4}] = lookup_keys(Q) 3268: end, [{{1,2}}, {{3,4}}, {{2,3}}])">>, 3269: 3270: <<"etsc(fun(E) -> 3271: Q = qlc:q([X || {{X,a}} <- ets:table(E), X =:= 3]), 3272: [3] = qlc:e(Q), 3273: [{3,a}] = lookup_keys(Q) 3274: end, [{{3,a}},{{3,b}}])">>, 3275: 3276: <<"etsc(fun(E) -> 3277: Q = qlc:q([X || {{X,Y},_Z} <- ets:table(E), 3278: X =:= 3, Y =:= a]), 3279: [3] = qlc:e(Q), 3280: [{3,a}] = lookup_keys(Q) 3281: end, [{{3,a},3}, {{4,a},3}])">>, 3282: 3283: <<"etsc(fun(E) -> 3284: Q = qlc:q([X || {{X,Y},_Z} <- ets:table(E), 3285: (X =:= 3) and (Y =:= a) 3286: or (X =:= 4) and (Y =:= a)]), 3287: [3,4] = qlc:e(Q), 3288: [{3,a}, {4,a}] = lookup_keys(Q) 3289: end, [{{3,a},3}, {{4,a},3}])">>, 3290: 3291: {cres, 3292: <<"etsc(fun(E) -> 3293: Q = qlc:q([X || {X} <- ets:table(E), 3294: (X =:= 3) and (X =:= a)]), 3295: [] = qlc:e(Q), 3296: false = lookup_keys(Q) 3297: end, [{3}, {4}])">>, 3298: {warnings,[{{3,44},qlc,nomatch_filter}]}}, 3299: 3300: <<"etsc(fun(E) -> 3301: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3302: X =:= 3, ((Y =:= a) or (Y =:= b))]), 3303: [3,3] = qlc:e(Q), 3304: [{3,a},{3,b}] = lists:sort(lookup_keys(Q)) 3305: end, [{{3,a}},{{2,b}},{{3,b}}])">>, 3306: 3307: <<"etsc(fun(E) -> 3308: Q = qlc:q([X || {X,Y} <- ets:table(E), 3309: ((X =:= 3) or (Y =:= 4)) and (X == a)]), 3310: [a] = qlc:e(Q), 3311: [a] = lookup_keys(Q) 3312: end, [{a,4},{3,3}])">>, 3313: 3314: <<"etsc(fun(E) -> 3315: Q = qlc:q([X || {X,Y} <- ets:table(E), 3316: (X =:= 3) or ((Y =:= 4) and (X == a))]), 3317: [3,a] = lists:sort(qlc:e(Q)), 3318: [3,a] = lookup_keys(Q) 3319: end, [{a,4},{3,3}])">>, 3320: 3321: <<"etsc(fun(E) -> 3322: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3323: (X =:= 3) or ((Y =:= 4) and (X == a))]), 3324: [3,a] = lists:sort(qlc:e(Q)), 3325: false = lookup_keys(Q) 3326: end, [{{3,a}},{{2,b}},{{a,4}}])">>, 3327: 3328: <<"etsc(fun(E) -> 3329: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3330: ((X =:= 3) or (Y =:= 4)) and (X == a)]), 3331: [a] = lists:sort(qlc:e(Q)), 3332: [{a,4}] = lookup_keys(Q) 3333: end, [{{3,a}},{{2,b}},{{a,4}}])">>, 3334: 3335: <<"etsc(fun(E) -> 3336: NoAnswers = 3*3*3+2*2*2, 3337: Q = qlc:q([{X,Y,Z} || 3338: {{X,Y,Z}} <- ets:table(E), 3339: (((X =:= 4) or (X =:= 5)) and 3340: ((Y =:= 4) or (Y =:= 5)) and 3341: ((Z =:= 4) or (Z =:= 5))) or 3342: (((X =:= 1) or (X =:= 2) or (X =:= 3)) and 3343: ((Y =:= 1) or (Y =:= 2) or (Y =:= 3)) and 3344: ((Z =:= 1) or (Z =:= 2) or (Z =:= 3)))], 3345: {max_lookup, NoAnswers}), 3346: {list, {table, _}, _} = i(Q), 3347: [{1,1,1},{2,2,2},{3,3,3}] = lists:sort(qlc:e(Q)), 3348: true = NoAnswers =:= length(lookup_keys(Q)) 3349: end, [{{1,1,1}},{{2,2,2}},{{3,3,3}},{{3,3,4}},{{4,1,1}}])">>, 3350: 3351: <<"etsc(fun(E) -> 3352: Q = qlc:q([{X,Y,Z} || 3353: {{X,Y,Z}} <- ets:table(E), 3354: (((X =:= 4) or (X =:= 5)) and 3355: ((Y =:= 4) or (Y =:= 5)) and 3356: ((Z =:= 4) or (Z =:= 5))) or 3357: (((X =:= 1) or (X =:= 2) or (X =:= 3)) and 3358: ((Y =:= 1) or (Y =:= 2) or (Y =:= 3)) and 3359: ((Z =:= 1) or (Z =:= 2) or (Z =:= 3)))], 3360: {max_lookup, 10}), 3361: [{1,1,1},{2,2,2},{3,3,3}] = lists:sort(qlc:e(Q)), 3362: {table,{ets,table,[_,[{traverse,{select,_}}]]}} = i(Q) 3363: end, [{{1,1,1}},{{2,2,2}},{{3,3,3}},{{3,3,4}},{{4,1,1}}])">>, 3364: 3365: <<"etsc(fun(E) -> 3366: Q = qlc:q([X || X={_,_,_} <- ets:table(E), 3367: element(1, X) =:= 3, element(2, X) == a]), 3368: [{3,a,s}] = qlc:e(Q), 3369: [3] = lookup_keys(Q) 3370: end, [{1,c,q},{2,b,r},{3,a,s}])">>, 3371: 3372: <<"etsc(fun(E) -> 3373: Q = qlc:q([X || X <- ets:table(E), 3374: element(0, X) =:= 3]), 3375: [] = qlc:e(Q), 3376: false = lookup_keys(Q) 3377: end, [{1},{2}])">>, 3378: 3379: <<"etsc(fun(E) -> 3380: F = fun(_) -> 3 end, 3381: %% No occurs check; X =:= F(X) is ignored. 3382: Q = qlc:q([X || {X} <- ets:table(E), 3383: X =:= 3, X =:= F(X)]), 3384: {qlc,_,[{generate,_,{list,{table,_},_}},_],[]} = i(Q), 3385: [3] = lists:sort(qlc:e(Q)), 3386: [3] = lookup_keys(Q) 3387: end, [{2},{3},{4}])">>, 3388: 3389: <<"etsc(fun(E) -> 3390: A = a, B = a, 3391: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3392: ((X =:= A) and (Y =:= B)) 3393: or ((X =:= B) and (Y =:= A))]), 3394: [a] = qlc:e(Q), 3395: %% keys are usorted, duplicate removed: 3396: [{a,a}] = lookup_keys(Q) 3397: end, [{{a,a}},{{b,b}}])">>, 3398: 3399: <<"etsc(fun(E) -> 3400: A = a, B = b, 3401: Q = qlc:q([X || {{X,Y}} <- ets:table(E), 3402: ((X =:= A) and (Y =:= B)) 3403: or ((X =:= B) and (Y =:= A))]), 3404: [a,b] = lists:sort(qlc:e(Q)), 3405: [{a,b},{b,a}] = lookup_keys(Q) 3406: end, [{{a,b}},{{b,a}},{{c,a}},{{d,b}}])">>, 3407: 3408: %% The atom 'fail' is recognized - lookup. 3409: <<"etsc(fun(E) -> 3410: Q = qlc:q([A || {A} <- ets:table(E), 3411: (A =:= 2) 3412: orelse fail 3413: ]), 3414: [2] = lists:sort(qlc:e(Q)), 3415: [2] = lookup_keys(Q) 3416: end, [{1},{2}])">> 3417: 3418: ], 3419: ?line run(Config, Ts), 3420: 3421: TsR = [ 3422: %% is_record/2,3: 3423: <<"etsc(fun(E) -> 3424: Q = qlc:q([element(1, X) || X <- ets:table(E), 3425: erlang:is_record(X, r, 2)]), 3426: [r] = qlc:e(Q), 3427: [r] = lookup_keys(Q) 3428: end, [{keypos,1}], [#r{}])">>, 3429: <<"etsc(fun(E) -> 3430: Q = qlc:q([element(1, X) || X <- ets:table(E), 3431: is_record(X, r, 2)]), 3432: [r] = qlc:e(Q), 3433: [r] = lookup_keys(Q) 3434: end, [{keypos,1}], [#r{}])">>, 3435: {cres, 3436: <<"etsc(fun(E) -> 3437: Q = qlc:q([element(1, X) || X <- ets:table(E), 3438: record(X, r)]), 3439: [r] = qlc:e(Q), 3440: [r] = lookup_keys(Q) 3441: end, [{keypos,1}], [#r{}])">>, 3442: {warnings,[{{4,45},erl_lint,{obsolete_guard,{record,2}}}]}}, 3443: <<"etsc(fun(E) -> 3444: Q = qlc:q([element(1, X) || X <- ets:table(E), 3445: erlang:is_record(X, r)]), 3446: [r] = qlc:e(Q), 3447: [r] = lookup_keys(Q) 3448: end, [{keypos,1}], [#r{}])">>, 3449: <<"etsc(fun(E) -> 3450: Q = qlc:q([element(1, X) || X <- ets:table(E), 3451: is_record(X, r)]), 3452: [r] = qlc:e(Q), 3453: [r] = lookup_keys(Q) 3454: end, [{keypos,1}], [#r{}])">> 3455: 3456: ], 3457: ?line run(Config, <<"-record(r, {a}).\n">>, TsR), 3458: 3459: Ts2 = [ 3460: <<"etsc(fun(E) -> 3461: Q0 = qlc:q([X || 3462: X <- ets:table(E), 3463: (element(1, X) =:= 1) or 3464: (element(1, X) =:= 2)], 3465: {cache,ets}), 3466: Q = qlc:q([{X,Y} || 3467: X <- [1,2], 3468: Y <- Q0]), 3469: {qlc,_,[{generate,_,{list,[1,2]}}, 3470: {generate,_,{table,_}}], []} = i(Q), 3471: [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = lists:sort(qlc:e(Q)), 3472: [1,2] = lookup_keys(Q) 3473: end, [{keypos,1}], [{1},{2}])">>, 3474: 3475: <<"etsc(fun(E) -> 3476: Q0 = qlc:q([X || 3477: X <- ets:table(E), 3478: (element(1, X) =:= 1) or 3479: (element(1, X) =:= 2)]), 3480: Q = qlc:q([{X,Y} || 3481: X <- [1,2], 3482: Y <- Q0], 3483: {cache,true}), 3484: {qlc,_,[{generate,_,{list,[1,2]}}, 3485: {generate,_,{table,_}}],[]} = i(Q), 3486: [1,2] = lookup_keys(Q) 3487: end, [{keypos,1}], [{1},{2}])">>, 3488: 3489: %% One introduced QLC expression (join, ms), and the cache option. 3490: <<"%% Match spec and lookup. The lookup is done twice, which might 3491: %% be confusing... 3492: etsc(fun(E) -> 3493: Q = qlc:q([{X,Y} || 3494: X <- [1,2], 3495: {Y} <- ets:table(E), 3496: (Y =:= 1) or (Y =:= 2)], 3497: []), 3498: [{1,1},{1,2},{2,1},{2,2}] = qlc:e(Q), 3499: {qlc,_,[{generate,_,{list,[1,2]}}, 3500: {generate,_,{list,{table,_},_}}],[]} = i(Q), 3501: [1,2] = lookup_keys(Q) 3502: end, [{keypos,1}], [{1},{2},{3}])">>, 3503: <<"%% The same as last example, but with cache. 3504: %% No cache needed (always one lookup only). 3505: etsc(fun(E) -> 3506: Q = qlc:q([{X,Y} || 3507: X <- [1,2], 3508: {Y} <- ets:table(E), 3509: (Y =:= 1) or (Y =:= 2)], 3510: [cache]), 3511: [{1,1},{1,2},{2,1},{2,2}] = qlc:e(Q), 3512: {qlc,_,[{generate,_,{list,[1,2]}}, 3513: {generate,_,{list,{table,_},_}}],[]} = i(Q), 3514: [1,2] = lookup_keys(Q) 3515: end, [{keypos,1}], [{1},{2},{3}])">>, 3516: 3517: <<"%% And again, this time only lookup, no mach spec. 3518: etsc(fun(E) -> 3519: Q = qlc:q([{X,Y} || 3520: X <- [1,2], 3521: Y <- ets:table(E), 3522: (element(1, Y) =:= 1) 3523: or (element(1, Y) =:= 2)], 3524: []), 3525: [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = qlc:e(Q), 3526: {qlc,_,[{generate,_,{list,[1,2]}}, 3527: {generate,_,{table,_}}],[]} = i(Q), 3528: [1,2] = lookup_keys(Q) 3529: end, [{keypos,1}], [{1},{2},{3}])">>, 3530: <<"%% As last one, but with cache. 3531: %% No cache needed (always one lookup only). 3532: etsc(fun(E) -> 3533: Q = qlc:q([{X,Y} || 3534: X <- [1,2], 3535: Y <- ets:table(E), 3536: (element(1, Y) =:= 1) 3537: or (element(1, Y) =:= 2)], 3538: [cache]), 3539: {qlc,_,[{generate,_,{list,[1,2]}}, 3540: {generate,_,{table,_}}],[]} = i(Q), 3541: [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = qlc:e(Q), 3542: [1,2] = lookup_keys(Q) 3543: end, [{keypos,1}], [{1},{2},{3}])">>, 3544: 3545: <<"%% Lookup only. No cache. 3546: etsc(fun(E) -> 3547: Q = qlc:q([{X,Y} || 3548: X <- [1,2], 3549: {Y=2} <- ets:table(E)], 3550: []), 3551: {qlc,_,[{generate,_,{list,[1,2]}}, 3552: {generate,_,{table,_}}],[]} = i(Q), 3553: [{1,2},{2,2}] = qlc:e(Q), 3554: [2] = lookup_keys(Q) 3555: end, [{keypos,1}], [{1},{2},{3}])">>, 3556: <<"%% Lookup only. No cache. 3557: etsc(fun(E) -> 3558: Q = qlc:q([{X,Y} || 3559: X <- [1,2], 3560: {Y=2} <- ets:table(E)], 3561: [cache]), 3562: {qlc,_,[{generate,_,{list,[1,2]}}, 3563: {generate,_,{table,_}}],[]} = i(Q), 3564: [{1,2},{2,2}] = qlc:e(Q), 3565: [2] = lookup_keys(Q) 3566: end, [{keypos,1}], [{1},{2},{3}])">>, 3567: 3568: <<"%% Matchspec only. No cache. 3569: etsc(fun(E) -> 3570: Q = qlc:q([{X,Y} || 3571: X <- [1,2], 3572: {Y} <- ets:table(E), 3573: Y > 1], 3574: []), 3575: {qlc,_,[{generate,_,{list,[1,2]}}, 3576: {generate,_, 3577: {table,{ets,_,[_,[{traverse,_}]]}}}],[]} = 3578: i(Q), 3579: [{1,2},{1,3},{2,2},{2,3}] = qlc:e(Q), 3580: false = lookup_keys(Q) 3581: end, [{keypos,1}], [{1},{2},{3}])">>, 3582: <<"%% Matchspec only. Cache 3583: etsc(fun(E) -> 3584: Q = qlc:q([{X,Y} || 3585: X <- [1,2], 3586: {Y} <- ets:table(E), 3587: Y > 1], 3588: [cache]), 3589: {qlc,_,[{generate,_,{list,[1,2]}}, 3590: {generate,_,{qlc,_, 3591: [{generate,_,{table,{ets,_,[_,[{traverse,_}]]}}}], 3592: [{cache,ets}]}}],[]} = i(Q), 3593: [{1,2},{1,3},{2,2},{2,3}] = qlc:e(Q), 3594: false = lookup_keys(Q) 3595: end, [{keypos,1}], [{1},{2},{3}])">>, 3596: <<"%% An empty list. Always unique and cached. 3597: Q = qlc:q([X || {X} <- [], X =:= 1, begin X > 0 end], 3598: [{cache,true},{unique,true}]), 3599: {qlc,_,[{generate,_,{list,[]}},_],[{unique,true}]} = i(Q), 3600: _ = qlc:info(Q), 3601: [] = qlc:e(Q)">>, 3602: <<"%% A list is always cached. 3603: Q = qlc:q([{X,Y} || Y <- [1,2], X <- [2,1,2]], 3604: [cache]), 3605: {qlc,_,[{generate,_,{list,[1,2]}}, 3606: {generate,_,{list,[2,1,2]}}],[]} = i(Q), 3607: [{2,1},{1,1},{2,1},{2,2},{1,2},{2,2}] = qlc:e(Q)">>, 3608: <<"%% But a processed list is never cached. 3609: Q = qlc:q([{X,Y} || Y <- [1,2], X <- [2,1,2], X > 1], 3610: [cache]), 3611: {qlc,_,[{generate,_, {list,[1,2]}}, 3612: {generate,_,{qlc,_, 3613: [{generate,_,{list,{list,[2,1,2]},_}}], 3614: [{cache,ets}]}}],[]} = i(Q), 3615: [{2,1},{2,1},{2,2},{2,2}] = qlc:e(Q)">>, 3616: <<"%% A bug fixed in R11B-2: coalescing simple_qlc:s works now. 3617: Q0 = qlc:q([X || {X} <- [{1},{2},{3}]], {cache, ets}), 3618: Q1 = qlc:q([X || X <- Q0], {cache, list}), 3619: Q = qlc:q([{Y,X} || Y <- [1,2], X <- Q1, X < 2], {cache, list}), 3620: {qlc,_,[{generate,_,{list,_}}, 3621: {generate,_,{qlc,_,[{generate,_,{list,{list,_},_}}], 3622: [{cache,ets}]}},_],[]} = i(Q), 3623: [{1,1},{2,1}] = qlc:e(Q)">>, 3624: <<"Q = qlc:q([{X,Y} || Y <- [1,2], X <- [1,2], X > 1], 3625: [cache,unique]), 3626: {qlc,_,[{generate,_,{list,[1,2]}}, 3627: {generate,_,{qlc,_, 3628: [{generate,_,{list,{list,[1,2]},_}}], 3629: [{cache,ets},{unique,true}]}}], 3630: [{unique,true}]} = i(Q), 3631: [{2,1},{2,2}] = qlc:e(Q)">>, 3632: <<"L = [1,2,3], 3633: QH1 = qlc:q([{X} || X <- L, X > 1]), 3634: QH2 = qlc:q([{X} || X <- QH1, X > 0], [cache]), 3635: [{{2}},{{3}}] = qlc:e(QH2), 3636: {list,{list,{list,L},_},_} = i(QH2)">>, 3637: <<"L = [1,2,3,1,2,3], 3638: QH1 = qlc:q([{X} || X <- L, X > 1]), 3639: QH2 = qlc:q([{X} || X <- QH1, X > 0], [cache,unique]), 3640: [{{2}},{{3}}] = qlc:e(QH2), 3641: {qlc,_,[{generate,_,{list,{list,{list,L},_},_}}], 3642: [{unique,true}]} = i(QH2)">> 3643: 3644: ], 3645: 3646: ?line run(Config, Ts2), 3647: 3648: LTs = [ 3649: <<"etsc(fun(E) -> 3650: Q = qlc:q([X || X <- ets:table(E), 3651: element(1, X) =:= 1], 3652: {lookup,true}), 3653: {table,L} = i(Q), 3654: true = is_list(L), 3655: [{1,a}] = qlc:e(Q), 3656: [1] = lookup_keys(Q) 3657: end, [{1,a},{2,b}])">>, 3658: <<"%% No lookup, use the match spec for traversal instead. 3659: etsc(fun(E) -> 3660: Q = qlc:q([X || X <- ets:table(E), 3661: element(1, X) =:= 1], 3662: {lookup,false}), 3663: {table,{ets,table,_}} = i(Q), 3664: [{1,a}] = qlc:e(Q), 3665: false = lookup_keys(Q) 3666: end, [{1,a},{2,b}])">>, 3667: <<"%% As last one. {max_lookup,0} has the same effect. 3668: etsc(fun(E) -> 3669: Q = qlc:q([X || X <- ets:table(E), 3670: element(1, X) =:= 1], 3671: {max_lookup,0}), 3672: {table,{ets,table,_}} = i(Q), 3673: [{1,a}] = qlc:e(Q), 3674: false = lookup_keys(Q) 3675: end, [{1,a},{2,b}])">> 3676: 3677: ], 3678: ?line run(Config, LTs), 3679: 3680: ok. 3681: 3682: lookup_rec(doc) -> 3683: "Lookup keys. With records."; 3684: lookup_rec(suite) -> []; 3685: lookup_rec(Config) when is_list(Config) -> 3686: Ts = [ 3687: <<"etsc(fun(E) -> 3688: Q = qlc:q([A || #r{a = A} <- ets:table(E), 3689: (A =:= 3) or (4 =:= A)]), 3690: [3] = qlc:e(Q), 3691: [3,4] = lookup_keys(Q) 3692: end, [{keypos,2}], [#r{a = a}, #r{a = 3}, #r{a = 5}])">>, 3693: 3694: {cres, 3695: <<"etsc(fun(E) -> 3696: Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E), 3697: (A =:= 3) or (4 =:= A)]), 3698: [] = qlc:e(Q), 3699: false = lookup_keys(Q) 3700: end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>, 3701: {warnings,[{{4,44},qlc,nomatch_filter}]}}, 3702: 3703: <<"%% Compares an integer and a float. 3704: etsc(fun(E) -> 3705: Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E), 3706: (A == 17) or (17.0 == A)]), 3707: [_] = qlc:e(Q), 3708: [_] = lookup_keys(Q) 3709: end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>, 3710: 3711: <<"%% Compares an integer and a float. 3712: %% There is a test in qlc_pt.erl (Op =:= '=:=', C1 =:= C2), but 3713: %% that case is handled in an earlier clause (unify ... E, E). 3714: etsc(fun(E) -> 3715: Q = qlc:q([A || #r{a = 17.0 = A} <- ets:table(E), 3716: (A =:= 17) or (17.0 =:= A)]), 3717: [_] = qlc:e(Q), 3718: [_] = lookup_keys(Q) 3719: end, [{keypos,2}], [#r{a = 17.0}, #r{a = 3}, #r{a = 5}])">>, 3720: 3721: <<"%% Matches an integer and a float. 3722: etsc(fun(E) -> 3723: Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E), 3724: (A =:= 17) or (17.0 =:= A)]), 3725: [_] = qlc:e(Q), 3726: [_] = lookup_keys(Q) 3727: end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>, 3728: 3729: <<"etsc(fun(E) -> 3730: F = fun(_) -> 17 end, 3731: Q = qlc:q([A || #r{a = A} <- ets:table(E), 3732: (F(A) =:= 3) and (A =:= 4)]), 3733: [] = qlc:e(Q), 3734: false = lookup_keys(Q) % F(A) could fail 3735: end, [{keypos,2}], [#r{a = 4}, #r{a = 3}, #r{a = 5}])">>, 3736: 3737: <<"etsc(fun(E) -> 3738: Q = qlc:q([X || {X} <- ets:table(E), 3739: #r{} == X]), 3740: [#r{}] = lists:sort(qlc:e(Q)), 3741: {call,_,_,[_,_]} = i(Q, {format, abstract_code}), 3742: [#r{}] = lookup_keys(Q) 3743: end, [{#r{}},{#r{a=foo}}])">>, 3744: 3745: <<"etsc(fun(E) -> 3746: Q = qlc:q([R#r.a || R <- ets:table(E), R#r.a =:= foo]), 3747: [foo] = qlc:e(Q), 3748: [_] = lookup_keys(Q) 3749: end, [{keypos,2}], [#r{a=foo}])">> 3750: ], 3751: ?line run(Config, <<"-record(r, {a}).\n">>, Ts), 3752: ok. 3753: 3754: indices(doc) -> 3755: "Using indices for lookup."; 3756: indices(suite) -> []; 3757: indices(Config) when is_list(Config) -> 3758: Ts = [ 3759: <<"L = [{1,a},{2,b},{3,c}], 3760: QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2]), 3761: (element(2, X) =:= a) 3762: or (b =:= element(2, X))]), 3763: {list, {table,{qlc_SUITE,list_keys,[[a,b],2,L]}}, _MS} = i(QH), 3764: [1,2] = qlc:eval(QH)">>, 3765: 3766: <<"L = [{1,a},{2,b},{3,c}], 3767: QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2]), 3768: begin (element(2, X) =:= a) 3769: or (b =:= element(2, X)) end]), 3770: {qlc,_,[{generate,_,{table,{call,_, 3771: {remote,_,_,{atom,_,the_list}},_}}},_],[]} 3772: = i(QH), 3773: [1,2] = qlc:eval(QH)">>, 3774: 3775: <<"L = [{1,a,q},{2,b,r},{3,c,s}], 3776: QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2,3]), 3777: (element(3, X) =:= q) 3778: or (r =:= element(3, X))]), 3779: {list, {table,{qlc_SUITE,list_keys, [[q,r],3,L]}}, _MS} = i(QH), 3780: [1,2] = qlc:eval(QH)">>, 3781: 3782: <<"L = [{1,a,q},{2,b,r},{3,c,s}], 3783: QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, 1, [2]), 3784: (element(3, X) =:= q) 3785: or (r =:= element(3, X))]), 3786: {qlc,_,[{generate,_,{table,{call,_,_,_}}}, 3787: _],[]} = i(QH), 3788: [1,2] = qlc:eval(QH)">>, 3789: 3790: <<"L = [{a,1},{b,2},{c,3}], 3791: QH = qlc:q([E || {K,I}=E <- qlc_SUITE:table(L, 1, [2]), 3792: ((K =:= a) or (K =:= b) or (K =:= c)) 3793: and ((I =:= 1) or (I =:= 2))], 3794: {max_lookup, 3}), 3795: {list, {table,{qlc_SUITE,list_keys,[[a,b,c],1,L]}}, _MS} = i(QH), 3796: [{a,1},{b,2}] = qlc:eval(QH)">>, 3797: 3798: <<"L = [{a,1},{b,2},{c,3}], 3799: QH = qlc:q([E || {K,I}=E <- qlc_SUITE:table(L, 1, [2]), 3800: ((K =:= a) or (K =:= b) or (K =:= c)) 3801: and ((I =:= 1) or (I =:= 2))], 3802: {max_lookup, 2}), 3803: {list, {table,{qlc_SUITE,list_keys, [[1,2],2,L]}}, _MS} = i(QH), 3804: [{a,1},{b,2}] = qlc:eval(QH)">>, 3805: 3806: <<"L = [{a,1,x,u},{b,2,y,v},{c,3,z,w}], 3807: QH = qlc:q([E || {K,I1,I2,I3}=E <- qlc_SUITE:table(L, 1, [2,3,4]), 3808: ((K =/= a) or (K =/= b) or (K =/= c)) 3809: and ((I1 =:= 1) or (I1 =:= 2) or 3810: (I1 =:= 3) or (I1 =:= 4)) 3811: and ((I2 =:= x) or (I2 =:= z)) 3812: and ((I3 =:= v) or (I3 =:= w))], 3813: {max_lookup, 5}), 3814: {list, {table,{qlc_SUITE,list_keys, [[x,z],3,L]}}, _MS} = i(QH), 3815: [{c,3,z,w}] = qlc:eval(QH)">> 3816: 3817: ], 3818: ?line run(Config, <<"-record(r, {a}).\n">>, Ts), 3819: ok. 3820: 3821: pre_fun(doc) -> 3822: "Test the table/2 callback functions parent_fun and stop_fun."; 3823: pre_fun(suite) -> []; 3824: pre_fun(Config) when is_list(Config) -> 3825: Ts = [ 3826: <<"PF = process_flag(trap_exit, true), 3827: %% cursor: table killing parent 3828: L = [{1,a},{2,b},{3,c}], 3829: F1 = fun() -> 3830: QH = qlc:q([element(1, X) || 3831: X <- qlc_SUITE:table_kill_parent(L, [2]), 3832: (element(2, X) =:= a) 3833: or (b =:= element(2, X))]), 3834: _ = qlc:info(QH), 3835: _ = qlc:cursor(QH) 3836: end, 3837: Pid1 = spawn_link(F1), 3838: receive {'EXIT', Pid1, killed} -> 3839: ok 3840: end, 3841: timer:sleep(1), 3842: process_flag(trap_exit, PF)">>, 3843: 3844: <<"PF = process_flag(trap_exit, true), 3845: %% eval without cursor: table killing parent 3846: L = [{1,a},{2,b},{3,c}], 3847: F2 = fun() -> 3848: QH = qlc:q([element(1, X) || 3849: X <- qlc_SUITE:table_kill_parent(L, [2]), 3850: (element(2, X) =:= a) 3851: or (b =:= element(2, X))]), 3852: _ = qlc:eval(QH) 3853: end, 3854: Pid2 = spawn_link(F2), 3855: receive {'EXIT', Pid2, killed} -> 3856: ok 3857: end, 3858: process_flag(trap_exit, PF)">>, 3859: 3860: <<"L = [{1,a},{2,b},{3,c}], 3861: QH = qlc:q([element(1, X) || 3862: X <- qlc_SUITE:table_parent_throws(L, [2]), 3863: (element(2, X) =:= a) 3864: or (b =:= element(2, X))]), 3865: _ = qlc:info(QH), 3866: {throw,thrown} = (catch {any_term,qlc:cursor(QH)}), 3867: {throw,thrown} = (catch {any_term,qlc:eval(QH)})">>, 3868: 3869: <<"L = [{1,a},{2,b},{3,c}], 3870: QH = qlc:q([element(1, X) || 3871: X <- qlc_SUITE:table_parent_exits(L, [2]), 3872: (element(2, X) =:= a) 3873: or (b =:= element(2, X))]), 3874: _ = qlc:info(QH), 3875: {'EXIT', {badarith,_}} = (catch qlc:cursor(QH)), 3876: {'EXIT', {badarith,_}} = (catch qlc:eval(QH))">>, 3877: 3878: <<"L = [{1,a},{2,b},{3,c}], 3879: QH = qlc:q([element(1, X) || 3880: X <- qlc_SUITE:table_bad_parent_fun(L, [2]), 3881: (element(2, X) =:= a) 3882: or (b =:= element(2, X))]), 3883: {'EXIT', {badarg,_}} = (catch qlc:cursor(QH)), 3884: {'EXIT', {badarg,_}} = (catch qlc:eval(QH))">>, 3885: 3886: <<"%% Very simple test of stop_fun. 3887: Ets = ets:new(apa, [public]), 3888: L = [{1,a},{2,b},{3,c}], 3889: H = qlc:q([X || {X,_} <- qlc_SUITE:stop_list(L, Ets)]), 3890: C = qlc:cursor(H), 3891: [{stop_fun,StopFun}] = ets:lookup(Ets, stop_fun), 3892: StopFun(), 3893: {'EXIT', {{qlc_cursor_pid_no_longer_exists, _}, _}} = 3894: (catch qlc:next_answers(C, all_remaining)), 3895: ets:delete(Ets)">> 3896: 3897: ], 3898: 3899: ?line run(Config, Ts), 3900: ok. 3901: 3902: skip_filters(doc) -> 3903: "Lookup keys. With records."; 3904: skip_filters(suite) -> []; 3905: skip_filters(Config) when is_list(Config) -> 3906: %% Skipped filters 3907: TsS = [ 3908: %% Cannot skip the filter. 3909: <<"etsc(fun(E) -> 3910: H = qlc:q([X || X <- ets:table(E), 3911: (element(1, X) =:= 1) xor (element(1, X) =:= 1)]), 3912: [] = qlc:eval(H), 3913: [1] = lookup_keys(H) 3914: end, [{keypos,1}], [{1},{2}])">>, 3915: 3916: %% The filter can be skipped. Just a lookup remains. 3917: <<"etsc(fun(E) -> 3918: H = qlc:q([X || X <- ets:table(E), 3919: (element(1, X) =:= 1) or (element(1, X) =:= 1)]), 3920: [{1}] = qlc:eval(H), 3921: {table, _} = i(H), 3922: [1] = lookup_keys(H) 3923: end, [{keypos,1}], [{1},{2}])">>, 3924: 3925: %% safe_unify fails on 3 and <<X:32>> 3926: <<"etsc(fun(E) -> 3927: H = qlc:q([X || X <- ets:table(E), 3928: (element(1, X) =:= 1) and (3 =:= <<X:32>>)]), 3929: [] = qlc:eval(H), 3930: [1] = lookup_keys(H) 3931: end, [{keypos,1}], [{1},{2}])">>, 3932: 3933: %% Two filters are skipped. 3934: <<"etsc(fun(E) -> 3935: Q = qlc:q([{B,C,D} || {A={C},B} <- ets:table(E), 3936: (A =:= {1}) or (A =:= {2}), 3937: (C =:= 1) or (C =:= 2), 3938: D <- [1,2]]), 3939: {qlc,_,[{generate,_,{table,_}},{generate,_,{list,[1,2]}}],[]} 3940: = i(Q), 3941: [{1,1,1},{1,1,2},{2,2,1},{2,2,2}] = lists:sort(qlc:eval(Q)), 3942: [{1},{2}] = lookup_keys(Q) 3943: end, [{{1},1},{{2},2},{{3},3}])">>, 3944: 3945: <<"etsc(fun(E) -> 3946: Q = qlc:q([{B,C} || {A={C},B} <- ets:table(E), 3947: (A =:= {1}) or (A =:= {2}), 3948: (C =:= 1) or (C =:= 2)]), 3949: {qlc,_,[{generate,_,{table,_}}],[]} = i(Q), 3950: [{1,1},{2,2}] = lists:sort(qlc:eval(Q)), 3951: [{1},{2}] = lookup_keys(Q) 3952: end, [{{1},1},{{2},2},{{3},3}])">>, 3953: 3954: %% Lookup. No match spec, no filter. 3955: <<"etsc(fun(E) -> 3956: Q = qlc:q([X || X <- ets:table(E), 3957: element(1, X) =:= 1]), 3958: {table, _} = i(Q), 3959: [{1}] = qlc:e(Q), 3960: [1] = lookup_keys(Q) 3961: end, [{1},{2}])">>, 3962: 3963: <<"etsc(fun(E) -> 3964: Q = qlc:q([{X,Y} || X <- ets:table(E), 3965: element(1, X) =:= 1, 3966: Y <- [1,2]]), 3967: {qlc,_,[{generate,_,{table,_}},{generate,_,{list,_}}],[]} 3968: = i(Q), 3969: [{{1},1},{{1},2}] = lists:sort(qlc:e(Q)), 3970: [1] = lookup_keys(Q) 3971: end, [{1},{2}])">>, 3972: 3973: <<"etsc(fun(E) -> 3974: Q = qlc:q([X || {X,Y} <- ets:table(E), 3975: X =:= a, 3976: X =:= Y]), 3977: {list,{table,_},_} = i(Q), 3978: [a] = qlc:e(Q), 3979: [a] = lookup_keys(Q) 3980: end, [{a,a},{b,c},{c,a}])">>, 3981: 3982: %% The imported variable (A) is never looked up in the current 3983: %% implementation. This means that the first filter cannot be skipped; 3984: %% the constant 'a' is looked up, and then the first filter evaluates 3985: %% to false. 3986: <<"etsc(fun(E) -> 3987: A = 3, 3988: Q = qlc:q([X || X <- ets:table(E), 3989: A == element(1,X), 3990: element(1,X) =:= a]), 3991: [] = qlc:e(Q), 3992: [a] = lookup_keys(Q) 3993: end, [{a},{b},{c}])">>, 3994: 3995: %% No lookup. 3996: {cres, 3997: <<"etsc(fun(E) -> 3998: Q = qlc:q([X || {X} <- ets:table(E), 3999: X =:= 1, 4000: X =:= 2]), 4001: {table, _} = i(Q), 4002: [] = qlc:e(Q), 4003: false = lookup_keys(Q) 4004: end, [{1,1},{2,0}])">>, 4005: {warnings,[{{4,37},qlc,nomatch_filter}]}}, 4006: 4007: <<"etsc(fun(E) -> 4008: Q = qlc:q([{A,B,C} || 4009: {A} <- ets:table(E), 4010: A =:= 1, 4011: {B} <- ets:table(E), 4012: B =:= 2, 4013: {C} <- ets:table(E), 4014: C =:= 3]), 4015: {qlc,_,[{generate,_,{list,{table,_},_}}, 4016: {generate,_,{list,{table,_},_}}, 4017: {generate,_,{list,{table,_},_}}],[]} = i(Q), 4018: [{1,2,3}] = qlc:e(Q), 4019: [1,2,3] = lookup_keys(Q) 4020: end, [{0},{1},{2},{3},{4}])">> 4021: 4022: ], 4023: ?line run(Config, TsS), 4024: 4025: Ts = [ 4026: <<"etsc(fun(E) -> 4027: H = qlc:q([X || {X,_} <- ets:table(E), 4028: X =:= 2]), 4029: {list,{table,_},_} = i(H), 4030: [2] = qlc:e(H) 4031: end, [{1,a},{2,b}])">>, 4032: 4033: <<"etsc(fun(E) -> 4034: H = qlc:q([X || {X,_} <- ets:table(E), 4035: ((X =:= 2) or (X =:= 1)) and (X > 1)]), 4036: {list,{table,_},_} = i(H), 4037: [2] = qlc:e(H) 4038: end, [{1,a},{2,b}])">>, 4039: 4040: <<"etsc(fun(E) -> 4041: H = qlc:q([X || {X,Y} <- ets:table(E), 4042: (X =:= 2) and (Y =:= b)]), 4043: {list,{table,_},_} = i(H), 4044: [2] = qlc:e(H) 4045: end, [{1,a},{2,b}])">>, 4046: 4047: <<"etsc(fun(E) -> 4048: H = qlc:q([X || X <- ets:table(E), 4049: (element(1,X) =:= 2) and (X =:= {2,b})]), 4050: {list,{table,_},_} = i(H), 4051: [{2,b}] = qlc:e(H) 4052: end, [{1,a},{2,b}])">>, 4053: 4054: <<"etsc(fun(E) -> 4055: H = qlc:q([{X,Y,Z,W} || 4056: {X,Y} <- ets:table(E), 4057: {Z,W} <- ets:table(E), 4058: (Y =:= 3) or (Y =:= 4)]), 4059: {qlc,_,[{generate,_,{table,{ets,table,_}}}, 4060: {generate,_,{table,{ets,table,_}}}],[]} = i(H), 4061: [{a,3,a,3},{a,3,b,5}] = lists:sort(qlc:e(H)) 4062: end, [{a,3},{b,5}])">>, 4063: 4064: <<"etsc(fun(E) -> 4065: H = qlc:q([{X,Y} || 4066: {X,Y=3} <- ets:table(E), % no matchspec 4067: %% Two columns restricted, but lookup anyway 4068: (X =:= a)]), 4069: {qlc,_,[{generate,_,{table,_}}],[]} = i(H), 4070: [{a,3}] = qlc:e(H) 4071: end, [{a,3},{b,4}])">>, 4072: 4073: <<"etsc(fun(E) -> 4074: V = 3, 4075: H = qlc:q([{X,Y} || 4076: {X,Y} <- ets:table(E), 4077: (Y =:= V)]), % imported variable, no lookup 4078: {table,{ets,table,_}} = i(H), 4079: [{a,3}] = qlc:e(H) 4080: end, [{a,3},{b,4}])">>, 4081: 4082: <<"etsc(fun(E) -> 4083: V = b, 4084: H = qlc:q([{X,Y} || 4085: {X,Y} <- ets:table(E), 4086: (X =:= V)]), % imported variable, lookup 4087: {list,{table,_},_} = i(H), 4088: [{b,4}] = qlc:e(H) 4089: end, [{a,3},{b,4}])">>, 4090: 4091: <<"H = qlc:q([{A,B} || {{A,B}} <- [{{1,a}},{{2,b}}], 4092: A =:= 1, 4093: B =:= a]), 4094: {list,{list,[_,_]},_} = i(H), 4095: [{1,a}] = qlc:e(H)">>, 4096: 4097: <<"etsc(fun(E) -> 4098: H = qlc:q([{A,B} || {{A,B}} <- ets:table(E), 4099: A =:= 1, 4100: B =:= a]), 4101: {list,{table,_},_} = i(H), 4102: [{1,a}] = qlc:e(H) 4103: end, [{{1,a}},{{2,b}}])">>, 4104: 4105: %% The filters are skipped, and the guards of the match specifications 4106: %% are skipped as well. Only the transformations of the matchspecs 4107: %% are kept. 4108: <<"etsc(fun(E1) -> 4109: etsc(fun(E2) -> 4110: H = qlc:q([{X,Y,Z,W} || 4111: {X,_}=Z <- ets:table(E1), 4112: W={Y} <- ets:table(E2), 4113: (X =:= 1) or (X =:= 2), 4114: (Y =:= a) or (Y =:= b)] 4115: ,{lookup,true} 4116: ), 4117: {qlc,_,[{generate,_,{list,{table,_}, 4118: [{{'$1','_'},[],['$_']}]}}, 4119: {generate,_,{list,{table,_}, 4120: [{{'$1'},[],['$_']}]}}],[]} 4121: = i(H), 4122: [{1,a,{1,a},{a}}, 4123: {1,b,{1,a},{b}}, 4124: {2,a,{2,b},{a}}, 4125: {2,b,{2,b},{b}}] = qlc:e(H) 4126: end, [{a},{b}]) 4127: end, [{1,a},{2,b}])">>, 4128: 4129: %% The same example again, but this time no match specs are run. 4130: <<"fun(Z) -> 4131: etsc(fun(E1) -> 4132: etsc(fun(E2) -> 4133: H = qlc:q([{X,Y} || 4134: Z > 2, 4135: X <- ets:table(E1), 4136: Y <- ets:table(E2), 4137: (element(1, X) =:= 1) or 4138: (element(1, X) =:= 2), 4139: (element(1, Y) =:= a) or 4140: (element(1, Y) =:= b)] 4141: ,{lookup,true} 4142: ), 4143: {qlc,_,[_,{generate,_,{table,_}}, 4144: {generate,_,{table,_}}],[]} = i(H), 4145: [{{1,a},{a}}, 4146: {{1,a},{b}}, 4147: {{2,b},{a}}, 4148: {{2,b},{b}}] = qlc:e(H) 4149: end, [{a},{b}]) 4150: end, [{1,a},{2,b}]) 4151: end(4)">>, 4152: 4153: %% Once again, this time with a join. 4154: <<"etsc(fun(E1) -> 4155: etsc(fun(E2) -> 4156: H = qlc:q([{X,Y,Z,W} || 4157: {X,V}=Z <- ets:table(E1), 4158: W={Y} <- ets:table(E2), 4159: (X =:= 1) or (X =:= 2), 4160: (Y =:= a) or (Y =:= b), 4161: Y =:= V] 4162: ,[{lookup,true},{join,merge}] 4163: ), 4164: {qlc,_,[{generate,_,{qlc,_, 4165: [{generate,_,{qlc,_,[{generate,_, 4166: {keysort,{list,{table,_},_},2,[]}}, 4167: _C1,_C2],[]}}, 4168: {generate,_, 4169: {qlc,_,[{generate, _, 4170: {keysort,{list,{table,_},_},1,[]}}, 4171: _C3], 4172: []}}, 4173: _], 4174: [{join,merge}]}},_],[]} = i(H), 4175: [{1,a,{1,a},{a}},{2,b,{2,b},{b}}] = 4176: lists:sort(qlc:e(H)) 4177: end, [{a},{b}]) 4178: end, [{1,a},{2,b}])">>, 4179: 4180: %% Filters 2 and 3 are not skipped. 4181: %% (Only one filter at a time is tried by the parse transform.) 4182: <<"etsc(fun(E) -> 4183: H = qlc:q([X || {{A,B}=X,Y} <- ets:table(E), % no matchspec 4184: Y =:= 3, 4185: A =:= 1, 4186: B =:= a]), 4187: 4188: {qlc,_,[{generate,_,{table,_}},_,_,_],[]}= i(H), 4189: [{1,a}] = qlc:e(H) 4190: end, [{{1,a},3},{{2,b},4}])">>, 4191: 4192: <<"etsc(fun(E) -> 4193: H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec 4194: (X =:= 3) and (X > 3)]), 4195: {qlc,_,[{generate,_,{table,_}},_],[]} = i(H), 4196: [] = qlc:e(H) 4197: end, [{3,a},{4,b}])">>, 4198: 4199: <<"etsc(fun(E) -> 4200: H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec 4201: (X =:= 3) or true]), 4202: {qlc,_,[{generate,_,{table,{ets,table,_}}},_],[]} = i(H), 4203: [3,4] = lists:sort(qlc:e(H)) 4204: end, [{3,a},{4,b}])">>, 4205: 4206: <<"etsc(fun(E) -> 4207: H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec 4208: (X =:= 3) or false]), 4209: {qlc,_,[{generate,_,{table,_}}],[]} = i(H), 4210: [3] = lists:sort(qlc:e(H)) 4211: end, [{3,a},{4,b}])">>, 4212: 4213: <<"etsc(fun(E) -> 4214: H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec 4215: (X =:= X) and (X =:= 3)]), 4216: {qlc,_,[{generate,_,{table,_}}],[]} = i(H), 4217: [3] = lists:sort(qlc:e(H)) 4218: end, [{3,a},{4,b}])">>, 4219: 4220: %% The order of filters matters. A guard filter cannot be used 4221: %% unless there are no non-guard filter placed before the guard 4222: %% filter that uses the guard filter's generator. There is 4223: %% more examples in join_filter(). 4224: <<"etsc(fun(E) -> 4225: %% Lookup. 4226: Q = qlc:q([{A,B,A} || 4227: {A=_,B} <- ets:table(E), % no match spec 4228: A =:= 1, 4229: begin 1/B > 0 end]), 4230: [{1,1,1}] = lists:sort(qlc:e(Q)) 4231: end, [{1,1},{2,0}])">>, 4232: <<"etsc(fun(E) -> 4233: %% No lookup. 4234: Q = qlc:q([{A,B,A} || 4235: {A=_,B} <- ets:table(E), % no match spec 4236: begin 1/B > 0 end, 4237: A =:= 1]), 4238: {'EXIT', _} = (catch qlc:e(Q)) 4239: end, [{1,1},{2,0}])">>, 4240: %% The same thing, with a match specification. 4241: <<"etsc(fun(E) -> 4242: Q = qlc:q([{A,B,A} || 4243: {A,B} <- ets:table(E), % match spec 4244: A < 2, 4245: begin 1/B > 0 end]), 4246: [{1,1,1}] = lists:sort(qlc:e(Q)) 4247: end, [{1,1},{2,0}])">>, 4248: <<"etsc(fun(E) -> 4249: Q = qlc:q([{A,B,A} || 4250: {A,B} <- ets:table(E), % match spec 4251: begin 1/B > 0 end, 4252: A < 2]), 4253: {'EXIT', _} = (catch qlc:e(Q)) 4254: end, [{1,1},{2,0}])">>, 4255: %% More examples, this time two tables. 4256: <<"etsc(fun(E) -> 4257: Q = qlc:q([{A,B,C,D} || 4258: {A,B} <- ets:table(E), % match spec 4259: A < 2, 4260: {C,D} <- ets:table(E), 4261: begin 1/B > 0 end, %\"invalidates\" next filter 4262: C =:= 1, 4263: begin 1/D > 0 end]), 4264: {qlc,_,[{generate,_,{table,{ets,table,_}}}, 4265: {generate,_,{table,{ets,table,_}}}, 4266: _,_,_],[]} = i(Q), 4267: [{1,1,1,1}] = lists:sort(qlc:e(Q)) 4268: end, [{1,1},{2,0}])">>, 4269: <<"etsc(fun(E) -> 4270: Q = qlc:q([{A,B,C,D} || 4271: {A,B} <- ets:table(E), 4272: {C,D} <- ets:table(E), 4273: begin 1/B > 0 end, % \"invalidates\" two filters 4274: A < 2, 4275: C =:= 1, 4276: begin 1/D > 0 end]), 4277: {qlc,_,[{generate,_,{table,{ets,table,_}}}, 4278: {generate,_,{table,{ets,table,_}}},_,_,_,_],[]} = i(Q), 4279: {'EXIT', _} = (catch qlc:e(Q)) 4280: end, [{1,1},{2,0}])">>, 4281: <<"%% There are objects in the ETS table, but none passes the filter. 4282: %% F() would not be run if it did not \"invalidate\" the following 4283: %% guards. 4284: etsc(fun(E) -> 4285: F = fun() -> [foo || A <- [0], 1/A] end, 4286: Q1 = qlc:q([X || {X} <- ets:table(E), 4287: F(), % \"invalidates\" next guard 4288: X =:= 17]), 4289: {'EXIT', _} = (catch qlc:e(Q1)) 4290: end, [{1},{2},{3}])">>, 4291: <<"%% The last example works just like this one: 4292: etsc(fun(E) -> 4293: F = fun() -> [foo || A <- [0], 1/A] end, 4294: Q1 = qlc:q([X || {X} <- ets:table(E), 4295: F(), 4296: begin X =:= 17 end]), 4297: {'EXIT', _} = (catch qlc:e(Q1)) 4298: end, [{1},{2},{3}])">> 4299: 4300: ], 4301: ?line run(Config, Ts), 4302: 4303: ok. 4304: 4305: 4306: ets(doc) -> 4307: "ets:table/1,2."; 4308: ets(suite) -> []; 4309: ets(Config) when is_list(Config) -> 4310: Ts = [ 4311: <<"E = ets:new(t, [ordered_set]), 4312: true = ets:insert(E, [{1},{2}]), 4313: {'EXIT', _} = 4314: (catch qlc:e(qlc:q([X || {X} <- ets:table(E, bad_option)]))), 4315: {'EXIT', _} = 4316: (catch qlc:e(qlc:q([X || {X} <- ets:table(E,{traverse,bad})]))), 4317: All = [{'$1',[],['$1']}], 4318: TravAll = {traverse,{select,All}}, 4319: [_, _] = qlc:e(qlc:q([X || {X} <- ets:table(E, TravAll)])), 4320: [_, _] = qlc:e(qlc:q([X || {X} <- ets:table(E,{traverse,select})])), 4321: [1,2] = 4322: qlc:e(qlc:q([X || {X} <- ets:table(E, {traverse, first_next})])), 4323: [2,1] = 4324: qlc:e(qlc:q([X || {X} <- ets:table(E, {traverse, last_prev})])), 4325: {table,{ets,table,[_,[{traverse,{select,_}},{n_objects,1}]]}} = 4326: i(qlc:q([X || {X} <- ets:table(E, {n_objects,1})])), 4327: {qlc,_,[{generate,_,{table,{ets,table,[_,{n_objects,1}]}}},_],[]} = 4328: i(qlc:q([X || {X} <- ets:table(E,{n_objects,1}), 4329: begin (X >= 1) or (X < 1) end])), 4330: {qlc,_,[{generate,_,{table,{ets,table,[_]}}},_],[]} = 4331: i(qlc:q([X || {X} <- ets:table(E), 4332: begin (X >= 1) or (X < 1) end])), 4333: ets:delete(E)">>, 4334: 4335: begin 4336: MS = ets:fun2ms(fun({X,Y}) when X > 1 -> {X,Y} end), 4337: [<<"E = ets:new(apa,[]), 4338: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 4339: MS = ">>, io_lib:format("~w", [MS]), <<", 4340: Q = qlc:q([X || {X,_} <- ets:table(E, {traverse, {select, MS}}), 4341: X =:= 1]), 4342: R = qlc:e(Q), 4343: ets:delete(E), 4344: [] = R">>] 4345: end 4346: 4347: ], 4348: 4349: ?line run(Config, Ts), 4350: ok. 4351: 4352: dets(doc) -> 4353: "dets:table/1,2."; 4354: dets(suite) -> []; 4355: dets(Config) when is_list(Config) -> 4356: dets:start(), 4357: T = t, 4358: Fname = filename(T, Config), 4359: Ts = [ 4360: [<<"T = t, Fname = \"">>, Fname, <<"\", 4361: file:delete(Fname), 4362: {ok, _} = dets:open_file(T, [{file,Fname}]), 4363: ok = dets:insert(T, [{1},{2}]), 4364: {'EXIT', _} = 4365: (catch qlc:e(qlc:q([X || {X} <- dets:table(T, bad_option)]))), 4366: {'EXIT', _} = 4367: (catch qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,bad})]))), 4368: {'EXIT', _} = 4369: (catch 4370: qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,last_prev})]))), 4371: All = [{'$1',[],['$1']}], 4372: TravAll = {traverse,{select,All}}, 4373: [_,_] = qlc:e(qlc:q([X || {X} <- dets:table(T, TravAll)])), 4374: [_,_] = qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,select})])), 4375: [_,_] = 4376: qlc:e(qlc:q([X || {X} <- dets:table(T, {traverse, first_next})])), 4377: {table,{dets,table,[T,[{traverse,{select,_}},{n_objects,1}]]}} = 4378: i(qlc:q([X || {X} <- dets:table(T, {n_objects,1})])), 4379: {qlc,_,[{generate,_,{table,{dets,table,[t,{n_objects,1}]}}},_],[]}= 4380: i(qlc:q([X || {X} <- dets:table(T,{n_objects,1}), 4381: begin (X >= 1) or (X < 1) end])), 4382: {qlc,_,[{generate,_,{table,{dets,table,[_]}}},_],[]} = 4383: i(qlc:q([X || {X} <- dets:table(T), 4384: begin (X >= 1) or (X < 1) end])), 4385: H = qlc:q([X || {X} <- dets:table(T, {n_objects, default}), 4386: begin (X =:= 1) or (X =:= 2) or (X =:= 3) end]), 4387: [1,2] = lists:sort(qlc:e(H)), 4388: {qlc,_,[{generate,_,{table,_}},_],[]} = i(H), 4389: 4390: H2 = qlc:q([X || {X} <- dets:table(T), (X =:= 1) or (X =:= 2)]), 4391: [1,2] = lists:sort(qlc:e(H2)), 4392: {list,{table,_},_} = i(H2), 4393: true = binary_to_list(<< 4394: \"ets:match_spec_run(lists:flatmap(fun(V)->dets:lookup(t,V)end,\" 4395: \"[1,2]),ets:match_spec_compile([{{'$1'},[],['$1']}]))\">>) 4396: == format_info(H2, true), 4397: 4398: H3 = qlc:q([X || {X} <- dets:table(T), (X =:= 1)]), 4399: [1] = qlc:e(H3), 4400: {list,{table,_},_} = i(H3), 4401: 4402: ok = dets:close(T), 4403: file:delete(\"">>, Fname, <<"\"), 4404: ok">>], 4405: 4406: begin 4407: MS = ets:fun2ms(fun({X,Y}) when X > 1 -> {X,Y} end), 4408: [<<"T = t, Fname = \"">>, Fname, <<"\", 4409: {ok, _} = dets:open_file(T, [{file,Fname}]), 4410: MS = ">>, io_lib:format("~w", [MS]), <<", 4411: ok = dets:insert(T, [{1,a},{2,b},{3,c}]), 4412: Q = qlc:q([X || {X,_} <- dets:table(T, {traverse, {select, MS}}), 4413: X =:= 1]), 4414: R = qlc:e(Q), 4415: ok = dets:close(T), 4416: file:delete(\"">>, Fname, <<"\"), 4417: [] = R">>] 4418: end, 4419: 4420: [<<"T = t, Fname = \"">>, Fname, <<"\", 4421: {ok, _} = dets:open_file(T, [{file,Fname}]), 4422: Objs = [{X} || X <- lists:seq(1,10)], 4423: ok = dets:insert(T, Objs), 4424: {ok, Where} = dets:where(T, {2}), 4425: ok = dets:close(T), 4426: qlc_SUITE:crash(Fname, Where), 4427: 4428: {ok, _} = dets:open_file(T, [{file,Fname}]), 4429: HT = qlc:q([X || {X} <- dets:table(T, {traverse, first_next})]), 4430: {'EXIT',{error,{{bad_object,_},_}}} = (catch qlc:e(HT)), 4431: _ = dets:close(T), 4432: 4433: {ok, _} = dets:open_file(T, [{file,Fname}]), 4434: HMS = qlc:q([X || {X} <- dets:table(T, {traverse, select})]), 4435: {error,{{bad_object,_},_}} = qlc:e(HMS), 4436: _ = dets:close(T), 4437: 4438: {ok, _} = dets:open_file(T, [{file,Fname}]), 4439: HLU = qlc:q([X || {X} <- dets:table(T), X =:= 2]), 4440: {error,{{bad_object,_},_}} = qlc:e(HLU), 4441: _ = dets:close(T), 4442: 4443: file:delete(Fname)">>] 4444: 4445: ], 4446: 4447: ?line run(Config, Ts), 4448: _ = file:delete(Fname), 4449: ok. 4450: 4451: 4452: join_option(doc) -> 4453: "The 'join' option (any, lookup, merge, nested_loop). Also cache/unique."; 4454: join_option(suite) -> []; 4455: join_option(Config) when is_list(Config) -> 4456: Ts = [ 4457: <<"Q1 = qlc:q([X || X <- [1,2,3]],{join,merge}), 4458: {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:info(Q1)}), 4459: {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:e(Q1)}), 4460: 4461: Q2 = qlc:q([X || X <- [1,2,3], X > 1],{join,merge}), 4462: {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:info(Q2)}), 4463: {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:e(Q2)}), 4464: 4465: Q3 = qlc:q([{X,Y} || 4466: {X} <- [{1},{2},{3}], 4467: {Y} <- [{a},{b},{c}], 4468: X =:= Y], 4469: {join, merge}), 4470: 4471: {1,0,0,2} = join_info(Q3), 4472: [] = qlc:e(Q3), 4473: 4474: Q4 = qlc:q([{X,Y} || 4475: {X} <- [{1},{2},{3}], 4476: {Y} <- [{a},{b},{c}], 4477: X > Y], 4478: {join, lookup}), 4479: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q4)}), 4480: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q4)}), 4481: 4482: Q5 = qlc:q([{X,Y} || 4483: {X} <- [{1},{2},{3}], 4484: {Y} <- [{3},{4},{5}], 4485: X == Y], 4486: {join, merge}), 4487: [{3,3}] = qlc:e(Q5), 4488: 4489: Q6 = qlc:q([{X,Y} || 4490: {X} <- [{1},{2},{3}], 4491: {Y} <- [{3},{4},{5}], 4492: X == Y], 4493: {join, lookup}), 4494: {'EXIT', {cannot_carry_out_join, _}} = (catch {foo, qlc:info(Q6)}), 4495: {'EXIT', {cannot_carry_out_join, _}} = (catch {foo, qlc:e(Q6)}), 4496: 4497: Q7 = qlc:q([{X,Y} || 4498: {X} <- [{1},{2},{3}], 4499: {Y} <- [{3},{4},{5}], 4500: X == Y], 4501: {join, nested_loop}), 4502: {0,0,1,0} = join_info(Q7), 4503: [{3,3}] = qlc:e(Q7), 4504: 4505: Q8 = qlc:q([{X,Y} || 4506: {X} <- [{1},{2},{3}], 4507: {Y} <- [{3},{4},{5}], 4508: X =:= Y], 4509: {join, nested_loop}), 4510: {0,0,1,0} = join_info(Q8), 4511: [{3,3}] = qlc:e(Q8), 4512: 4513: %% Only guards are inspected... 4514: Q9 = qlc:q([{X,Y} || 4515: {X} <- [{1},{2},{3}], 4516: {Y} <- [{3},{4},{5}], 4517: begin X =:= Y end], 4518: {join, nested_loop}), 4519: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q9)}), 4520: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q9)}), 4521: 4522: Q10 = qlc:q([{X,Y} || 4523: {X} <- [{1},{2},{3}], 4524: {Y} <- [{3},{4},{5}], 4525: X < Y], 4526: {join, nested_loop}), 4527: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q10)}), 4528: {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q10)}), 4529: 4530: F = fun(J) -> qlc:q([X || X <- [1,2]], {join,J}) end, 4531: {'EXIT', {no_join_to_carry_out, _}} = 4532: (catch {foo, qlc:e(F(merge))}), 4533: {'EXIT', {no_join_to_carry_out, _}} = 4534: (catch {foo, qlc:e(F(lookup))}), 4535: {'EXIT', {no_join_to_carry_out, _}} = 4536: (catch {foo, qlc:e(F(nested_loop))}), 4537: [1,2] = qlc:e(F(any)), 4538: 4539: %% No join of columns in the same table. 4540: Q11 = qlc:q([{X,Y} || {a = X, X = Y} <- [{a,1},{a,a},{a,3},{a,a}]], 4541: {join,merge}), 4542: {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q11)), 4543: Q12 = qlc:q([{X,Y} || {X = a, X = Y} <- [{a,1},{a,a},{a,3},{a,a}]], 4544: {join,merge}), 4545: {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q12)), 4546: %% X and Y are \"equal\" (same constants), but must not be joined. 4547: Q13 = qlc:q([{X,Y} || {X,_Z} <- [{a,1},{a,2},{b,1},{b,2}], 4548: {Y} <- [{a}], 4549: (X =:= a) and (Y =:= b) or 4550: (X =:= b) and (Y =:= a)], 4551: {join,merge}), 4552: {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q13)) 4553: 4554: ">>, 4555: 4556: <<"Q1 = qlc:q([X || X <- [1,2,3]], {lookup,true}), 4557: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q1)}), 4558: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q1)}), 4559: Q2 = qlc:q([{X,Y} || X <- [1,2,3], Y <- [x,y,z]], lookup), 4560: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q2)}), 4561: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q2)}), 4562: Q3 = qlc:q([X || {X} <- [{1},{2},{3}]], {lookup,true}), 4563: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q3)}), 4564: {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q3)}), 4565: 4566: E1 = create_ets(1, 10), 4567: Q4 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], lookup), 4568: {match_spec, _} = strip_qlc_call(Q4), 4569: [{3,3}] = qlc:e(Q4), 4570: Q5 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], {lookup,false}), 4571: {table, ets, _} = strip_qlc_call(Q5), 4572: [{3,3}] = qlc:e(Q5), 4573: Q6 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], {lookup,any}), 4574: {match_spec, _} = strip_qlc_call(Q6), 4575: [{3,3}] = qlc:e(Q6), 4576: ets:delete(E1)">> 4577: 4578: ], 4579: ?line run(Config, Ts), 4580: 4581: %% The 'cache' and 'unique' options of qlc/2 affects join. 4582: CUTs = [ 4583: <<"L1 = [1,2], 4584: L2 = [{1,a},{2,b}], 4585: L3 = [{a,1},{b,2}], 4586: Q = qlc:q([{X,Y,Z} || 4587: Z <- L1, 4588: {X,_} <- L2, 4589: {_,Y} <- L3, 4590: X =:= Y], 4591: [cache, unique]), 4592: {qlc,_, 4593: [{generate,_,{list,L1}}, 4594: {generate,_,{qlc,_,[{generate,_, 4595: {qlc,_,[{generate,_,{keysort,{list,L2},1,[]}}],[]}}, 4596: {generate,_,{qlc,_, 4597: [{generate,_,{keysort,{list,L3},2,[]}}],[]}},_], 4598: [{join,merge},{cache,ets},{unique,true}]}},_], 4599: [{unique,true}]} = i(Q), 4600: [{1,1,1},{2,2,1},{1,1,2},{2,2,2}] = qlc:e(Q)">>, 4601: <<"L1 = [1,2], 4602: L2 = [{1,a},{2,b}], 4603: L3 = [{a,1},{b,2}], 4604: Q = qlc:q([{X,Y,Z} || 4605: Z <- L1, 4606: {X,_} <- L2, 4607: {_,Y} <- L3, 4608: X =:= Y], 4609: []), 4610: Options = [{cache_all,ets}, unique_all], 4611: {qlc,_,[{generate,_,{qlc,_,[{generate,_,{list,L1}}], 4612: [{unique,true}]}}, 4613: {generate,_,{qlc,_, 4614: [{generate,_,{qlc,_,[{generate,_, 4615: {keysort,{qlc,_,[{generate,_,{list,L2}}], 4616: [{cache,ets},{unique,true}]}, 4617: 1,[]}}],[]}}, 4618: {generate,_,{qlc,_, 4619: [{generate,_,{keysort, 4620: {qlc,_,[{generate,_,{list,L3}}], 4621: [{cache,ets},{unique,true}]}, 4622: 2,[]}}],[]}},_], 4623: [{join,merge},{cache,ets},{unique,true}]}}, 4624: _],[{unique,true}]} = i(Q, Options), 4625: [{1,1,1},{2,2,1},{1,1,2},{2,2,2}] = qlc:e(Q, Options)">> 4626: ], 4627: ?line run(Config, CUTs), 4628: 4629: ok. 4630: 4631: join_filter(doc) -> 4632: "Various aspects of filters and join."; 4633: join_filter(suite) -> []; 4634: join_filter(Config) when is_list(Config) -> 4635: Ts = [ 4636: <<"E1 = create_ets(1, 10), 4637: Q = qlc:q([X || {X,_} <- ets:table(E1), 4638: begin A = X * X end, % ej true (?) 4639: X >= A]), 4640: {'EXIT', _} = (catch qlc:e(Q)), 4641: ets:delete(E1)">>, 4642: 4643: %% The order of filters matters. See also skip_filters(). 4644: <<"Q = qlc:q([{X,Y} || {X,Y} <- [{a,1},{b,2}], 4645: {Z,W} <- [{a,1},{c,0}], 4646: X =:= Z, 4647: begin Y/W > 0 end]), 4648: [{a,1}] = qlc:e(Q)">>, 4649: <<"Q = qlc:q([{X,Y} || {X,Y} <- [{a,1},{b,2}], 4650: {Z,W} <- [{a,1},{c,0}], 4651: begin Y/W > 0 end, 4652: X =:= Z]), 4653: {'EXIT', _} = (catch qlc:e(Q))">>, 4654: 4655: <<"etsc(fun(E1) -> 4656: etsc(fun(E2) -> 4657: F = fun() -> [foo || A <- [0], 1/A] end, 4658: Q1 = qlc:q([X || {X} <- ets:table(E1), 4659: {Y} <- ets:table(E2), 4660: F(), % invalidates next filter 4661: X =:= Y]), 4662: {qlc,_,[{generate,_,{table,{ets,table,_}}}, 4663: {generate,_,{table,{ets,table,_}}},_,_], 4664: []} = i(Q1), 4665: {'EXIT', _} = (catch qlc:e(Q1)) 4666: end, [{1},{2},{3}]) 4667: end, [{a},{b},{c}])">> 4668: 4669: ], 4670: ?line run(Config, Ts), 4671: ok. 4672: 4673: join_lookup(doc) -> 4674: "Lookup join."; 4675: join_lookup(suite) -> []; 4676: join_lookup(Config) when is_list(Config) -> 4677: Ts = [ 4678: <<"E1 = create_ets(1, 10), 4679: E2 = create_ets(5, 15), 4680: Q = qlc:q([{X,Y} || {_,Y} <- ets:table(E2), 4681: {X,_} <- ets:table(E1), 4682: X =:= Y], [{join,lookup}]), 4683: {0,1,0,0} = join_info_count(Q), 4684: R = qlc:e(Q), 4685: ets:delete(E1), 4686: ets:delete(E2), 4687: [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = lists:sort(R)">>, 4688: 4689: <<"E1 = create_ets(1, 10), 4690: E2 = create_ets(5, 15), 4691: F = fun(J) -> qlc:q([{X,Y} || {X,_} <- ets:table(E1), 4692: {_,Y} <- ets:table(E2), 4693: X =:= Y], {join, J}) 4694: end, 4695: Q = F(lookup), 4696: {0,1,0,0} = join_info_count(Q), 4697: R = qlc:e(Q), 4698: ets:delete(E1), 4699: ets:delete(E2), 4700: [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = lists:sort(R)">>, 4701: 4702: <<"etsc(fun(E1) -> 4703: E2 = qlc_SUITE:table([{1,a},{a},{1,b},{b}], 2, []), 4704: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), % (1) 4705: {_,Z} <- E2, % (2) 4706: (Z =:= Y) and (X =:= a) 4707: or 4708: (Z =:= Y) and (X =:= b)]), 4709: %% Cannot look up in (1) (X is keypos). Can look up (2). 4710: %% Lookup-join: traverse (1), look up in (2). 4711: {0,1,0,0} = join_info_count(Q), 4712: [{a,a},{b,a}] = qlc:e(Q) 4713: end, [{a,a},{b,a},{c,3},{d,4}])">>, 4714: 4715: <<"%% The pattern {X,_} is used to filter out looked up objects. 4716: etsc(fun(E) -> 4717: Q = qlc:q([X || {X,_} <- ets:table(E), 4718: Y <- [{a,b},{c,d},{1,2},{3,4}], 4719: X =:= element(1, Y)]), 4720: {0,1,0,0} = join_info_count(Q), 4721: [1] = qlc:e(Q) 4722: end, [{1,2},{3}])">>, 4723: 4724: <<"E = ets:new(e, [bag,{keypos,2}]), 4725: L = lists:sort([{a,1},{b,1},{c,1},{d,1}, 4726: {aa,2},{bb,2},{cc,2},{dd,2}]), 4727: true = ets:insert(E, L ++ [{aaa,1,1},{bbb,2,2},{ccc,3,3}]), 4728: Q = qlc:q([Z || {_,Y}=Z <- ets:table(E), 4729: {X} <- [{X} || X <- lists:seq(0, 10)], 4730: X =:= Y]), 4731: {0,1,0,0} = join_info_count(Q), 4732: R = qlc:e(Q), 4733: ets:delete(E), 4734: L = lists:sort(R)">>, 4735: 4736: <<"E = ets:new(e, [bag,{keypos,2}]), 4737: L = lists:sort([{a,1},{b,1},{c,1},{d,1}, 4738: {aa,2},{bb,2},{cc,2},{dd,2}]), 4739: true = ets:insert(E, L ++ [{aaa,1,1},{bbb,2,2},{ccc,3,3}]), 4740: Q = qlc:q([Z || {X} <- [{X} || X <- lists:seq(0, 10)], 4741: {_,Y}=Z <- ets:table(E), 4742: X =:= Y]), 4743: {0,1,0,0} = join_info_count(Q), 4744: R = qlc:e(Q), 4745: ets:delete(E), 4746: L = lists:sort(R)">>, 4747: 4748: <<"Q = qlc:q([{XX,YY} || 4749: {XX,X} <- [{b,1},{c,3}], 4750: {Y,YY} <- qlc_SUITE:table_lookup_error([{1,a}]), 4751: X =:= Y], 4752: {join,lookup}), 4753: {error, lookup, failed} = qlc:e(Q)">>, 4754: 4755: <<"E = create_ets(1, 10), 4756: Q = qlc:q([{X,Y} || 4757: {X,_} <- ets:table(E), 4758: {_,Y} <- qlc_SUITE:table_error([{a,1}], 1, err), 4759: X =:= Y]), 4760: {0,1,0,0} = join_info_count(Q), 4761: err = qlc:e(Q), 4762: ets:delete(E)">> 4763: 4764: ], 4765: ?line run(Config, Ts), 4766: ok. 4767: 4768: join_merge(doc) -> 4769: "Merge join."; 4770: join_merge(suite) -> []; 4771: join_merge(Config) when is_list(Config) -> 4772: Ts = [ 4773: <<"Q = qlc:q([{X,Y} || {X} <- [], {Y} <- [{1}], X =:= Y], 4774: {join,merge}), 4775: [] = qlc:e(Q) 4776: ">>, 4777: 4778: <<"Q = qlc:q([{X,Y} || {X} <- [{1}], {Y} <- [], X =:= Y], 4779: {join,merge}), 4780: [] = qlc:e(Q) 4781: ">>, 4782: 4783: <<"Q = qlc:q([{X,Y} || {X} <- [{1},{1},{1}], 4784: {Y} <- [{1},{1},{1}], X =:= Y], 4785: {join,merge}), 4786: 9 = length(qlc:e(Q)) 4787: ">>, 4788: 4789: <<"%% Two merge joins possible. 4790: Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [{1,a},{1,b},{1,c}], 4791: {Z,W} <- [{1,a},{1,b},{1,c}], 4792: X =:= Z, 4793: Y =:= W]), 4794: {qlc,_,[{generate,_, 4795: {qlc,_, 4796: [{generate,_, 4797: {qlc,_,[{generate,_,{keysort,{list,_},C,[]}}],[]}}, 4798: {generate,_, 4799: {qlc,_,[{generate,_,{keysort,{list,_},C,[]}}],[]}}, 4800: _], 4801: [{join,merge}]}}, 4802: _,_],[]} = qlc:info(Q, {format,debug}), 4803: [{1,a,1,a},{1,b,1,b},{1,c,1,c}] = qlc:e(Q)">>, 4804: 4805: <<"%% As the last one, but comparison. 4806: Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [{1,a},{1,b},{1,c}], 4807: {Z,W} <- [{1,a},{1,b},{1,c}], 4808: X == Z, % skipped 4809: Y =:= W]), 4810: {qlc,_,[{generate,_, 4811: {qlc,_, 4812: [{generate,_, 4813: {qlc,_,[{generate,_,{keysort,{list,_},1,[]}}],[]}}, 4814: {generate,_, 4815: {qlc,_,[{generate,_,{keysort,{list,_},1,[]}}],[]}}, 4816: _], 4817: [{join,merge}]}}, 4818: _],[]} = qlc:info(Q, {format,debug}), 4819: [{1,a,1,a},{1,b,1,b},{1,c,1,c}] = qlc:e(Q)">>, 4820: 4821: <<"%% This is no join. 4822: Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [], {Z,W} <- [], 4823: X =:= Y, Z =:= W]), 4824: {0,0,0,0} = join_info_count(Q)">>, 4825: 4826: <<"%% Used to replace empty ETS tables with [], but that won't work. 4827: E1 = ets:new(e1, []), 4828: E2 = ets:new(e2, []), 4829: Q = qlc:q([{X,Z,W} || 4830: {X, Z} <- ets:table(E1), 4831: {W, Y} <- ets:table(E2), 4832: X =:= Y], 4833: {join, lookup}), 4834: [] = qlc:e(Q), 4835: ets:delete(E1), 4836: ets:delete(E2)">>, 4837: 4838: <<"Q = qlc:q([{X,Y} || {X} <- [{3},{1},{0}], 4839: {Y} <- [{1},{2},{3}], 4840: X =:= Y]), 4841: {1,0,0,2} = join_info_count(Q), 4842: [{1,1},{3,3}] = qlc:e(Q)">>, 4843: 4844: <<"QH = qlc:q([{X,Y,Z,W} || {X,Y} <- [{3,c},{2,b},{1,a}], 4845: {Z,W} <- [{2,b},{4,d},{5,e},{3,c}], 4846: X =:= Z, 4847: Y =:= W]), 4848: {1,0,0,2} = join_info_count(QH), 4849: [{2,b,2,b},{3,c,3,c}] = qlc:e(QH)">>, 4850: 4851: <<"%% QLC finds no join column at run time... 4852: QH = qlc:q([1 || X <- [{1,2,3},{4,5,6}], 4853: Y <- [{1,2},{3,4}], 4854: X =:= Y]), 4855: {0,0,0,0} = join_info_count(QH), 4856: [] = qlc:e(QH)">>, 4857: 4858: <<"QH = qlc:q([X || X <- [{1,2,3},{4,5,6}], 4859: Y <- [{1,2},{3,4}], 4860: element(1, X) =:= element(2, Y)]), 4861: {1,0,0,2} = join_info_count(QH), 4862: [{4,5,6}] = qlc:e(QH)">>, 4863: 4864: <<"Q = qlc:q([{A,X,Z,W} || 4865: A <- [a,b,c], 4866: {X,Z} <- [{a,1},{b,4},{c,6}], 4867: {W,Y} <- [{2,a},{3,b},{4,c}], 4868: X =:= Y], 4869: {cache, list}), 4870: _ = qlc:info(Q), 4871: [{a,a,1,2},{a,b,4,3},{a,c,6,4},{b,a,1,2},{b,b,4,3}, 4872: {b,c,6,4},{c,a,1,2},{c,b,4,3},{c,c,6,4}] = qlc:e(Q)">>, 4873: 4874: <<"Q = qlc:q([{X,Y} || 4875: {X,Z} <- [{a,1},{b,4},{c,6}], 4876: {W,Y} <- [{2,a},{3,b},{4,c}], 4877: Z > W, 4878: X =:= Y], 4879: {join,merge}), 4880: {qlc,_,[{generate,_,{qlc,_, 4881: [{generate,_, 4882: {qlc,_,[{generate,_,{keysort,_,1,[]}}],[]}}, 4883: {generate,_, 4884: {qlc,_,[{generate,_,{keysort,_,2,[]}}], 4885: []}},_],[{join,merge}]}}, 4886: _,_],[]} = i(Q), 4887: [{b,b},{c,c}] = qlc:e(Q)">>, 4888: 4889: <<"E1 = create_ets(1, 10), 4890: E2 = create_ets(5, 15), 4891: %% A match spec.; Q does not see Q1 and Q2 as lookup-tables. 4892: Q1 = qlc:q([X || X <- ets:table(E1)]), 4893: Q2 = qlc:q([X || X <- ets:table(E2)]), 4894: F = fun(J) -> qlc:q([{X,Y} || X <- Q1, 4895: Y <- Q2, 4896: element(1,X) =:= element(1,Y)], 4897: [{join,J}]) 4898: end, 4899: {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(F(lookup))), 4900: Q = F(merge), 4901: {1,0,0,2} = join_info(Q), 4902: R = lists:sort(qlc:e(Q)), 4903: ets:delete(E1), 4904: ets:delete(E2), 4905: true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {X,X})] =:= R 4906: ">>, 4907: 4908: <<"E1 = create_ets(1, 10), 4909: E2 = create_ets(5, 15), 4910: Q = qlc:q([{X,Y} || X <- ets:table(E1), 4911: Y <- ets:table(E2), 4912: element(1,X) =:= element(1,Y)], 4913: [{join,merge}]), 4914: {1,0,0,2} = join_info(Q), 4915: R = lists:sort(qlc:e(Q)), 4916: ets:delete(E1), 4917: ets:delete(E2), 4918: true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {X,X})] =:= R 4919: ">>, 4920: 4921: <<"E1 = create_ets(1, 10), 4922: E2 = create_ets(5, 15), 4923: Q1 = qlc:q([Y || X <- ets:table(E1), begin Y = {X}, true end]), 4924: %% A match spec.; Q does not see Q2 as a lookup-table. 4925: %% 4926: %% OTP-6673: lookup join is considered but since there is no 4927: %% filter that can do the job of Q2, lookup join is not an option.. 4928: Q2 = qlc:q([{X} || X <- ets:table(E2)]), 4929: F = fun(J) -> 4930: qlc:q([{X,Y} || X <- Q1, 4931: Y <- Q2, 4932: element(1,X) =:= element(1,Y)], 4933: [{join,J}]) 4934: end, 4935: {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(F(lookup))), 4936: Q = F(any), 4937: {1,0,0,2} = join_info(Q), 4938: R = lists:sort(qlc:e(Q)), 4939: ets:delete(E1), 4940: ets:delete(E2), 4941: true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {{X,X}})] =:= R 4942: ">>, 4943: 4944: <<"L1 = [{1,a},{2,a},{1,b},{2,b},{1,c},{2,c}], 4945: L2 = [{b,Y} || Y <- lists:seq(1, 10000)], 4946: F = fun(J) -> 4947: Q = qlc:q([{XX,YY} || 4948: {X,XX} <- L1, 4949: {YY,Y} <- L2, 4950: X == Y], 4951: {join,J}), 4952: qlc:q([{XX1,YY1,XX2,YY2} || 4953: {XX1,YY1} <- Q, 4954: {XX2,YY2} <- Q]) 4955: end, 4956: Qm = F(merge), 4957: Qn = F(nested_loop), 4958: true = lists:sort(qlc:e(Qm)) =:= lists:sort(qlc:e(Qn))">>, 4959: 4960: <<"L1 = [{{1,a},2},{{3,c},4}], 4961: L2 = [{a,{1,a}},{c,{4,d}}], 4962: Q = qlc:q([{X,Y} || {X,_} <- L1, 4963: {_,{Y,Z}} <- L2, 4964: X == {Y,Z} 4965: ]), 4966: {qlc,_,[{generate,_,{qlc,_, 4967: [{generate,_, 4968: {qlc,_,[{generate,_,{keysort,{list,L1},1,[]}}],[]}}, 4969: {generate,_, 4970: {qlc,_,[{generate,_,{keysort,{list,L2},2,[]}}],[]}}, 4971: _], 4972: [{join,merge}]}}],[]} = i(Q), 4973: [{{1,a},1}] = qlc:e(Q)">>, 4974: 4975: <<"etsc(fun(E1) -> 4976: etsc(fun(E2) -> 4977: Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), % (1) 4978: {Z} <- ets:table(E2), % (2) 4979: (Z =:= X) and 4980: (Y =:= a) and 4981: (X =:= Y) or 4982: (Y =:= b) and 4983: (Z =:= Y)]), 4984: %% Cannot look up in (1) (X is keypos). Can look up (2). 4985: %% Lookup join not possible (cannot look up in (1)). 4986: %% Merge join is possible (after lookup in (2)). 4987: {1,0,0,2} = join_info_count(Q), 4988: {qlc,_, 4989: [{generate,_, 4990: {qlc,_,[{generate,_, 4991: {qlc,_,[{generate,_, 4992: {keysort, 4993: {table,{ets,table,_}}, 4994: 2,[]}},_C1],[]}}, 4995: {generate,_, 4996: {qlc,_,[{generate,_, 4997: {keysort,{table,_},1,[]}},_C2], 4998: []}}, 4999: _],[{join,merge}]}},_],[]} = i(Q), 5000: [{a,a}] = qlc:e(Q) 5001: end, [{a}]) 5002: end, [{a,1},{a,a},{b,1},{b,2}])">>, 5003: 5004: <<"Q = qlc:q([{G1,G2} || 5005: G1<- [{1}], 5006: G2 <- [{1}], 5007: element(1, G1) =:= element(1, G2)]), 5008: {1,0,0,2} = join_info(Q), 5009: [{{1},{1}}] = qlc:e(Q)">>, 5010: 5011: <<"Q = qlc:q([{X,Y} || 5012: X <- [{1}], 5013: Y <- [{1}], 5014: element(1, X) =:= element(1, Y)], 5015: {join,merge}), 5016: {1,0,0,2} = join_info(Q), 5017: [{{1},{1}}] = qlc:e(Q)">>, 5018: 5019: <<"%% Generator after the join-filter. 5020: Q = qlc:q([Z || 5021: {X} <- [{1},{2},{3}], 5022: {Y} <- [{2},{3},{4}], 5023: X =:= Y, 5024: Z <- [1,2]]), 5025: {qlc,_, 5026: [{generate,_,{qlc,_, 5027: [{generate,_,{qlc,_, 5028: [{generate,_,{keysort,{list,[{1},{2},{3}]},1,[]}}],[]}}, 5029: {generate,_,{qlc,_, 5030: [{generate,_,{keysort,{list,_},1,[]}}],[]}},_], 5031: [{join,merge}]}}, _,{generate,_,{list,_}}],[]} = i(Q), 5032: [1,2,1,2] = qlc:e(Q)">>, 5033: 5034: <<"%% X and W occur twice in the pattern of the extra join handle. 5035: Q = qlc:q([{Z,W} || 5036: {X,Z,X} <- [{1,2,1},{1,2,2}], 5037: {W,Y,W} <- [{a,1,a}], 5038: X =:= Y]), 5039: [{2,a}] = qlc:e(Q)">> 5040: 5041: ], 5042: ?line run(Config, Ts), 5043: 5044: %% Small examples. Returning an error term. 5045: ETs = [ 5046: <<"F = fun(M) -> 5047: qlc:q([{XX,YY} || 5048: {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}], 5049: {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b},{2,bb},{2,bbb}, 5050: {3,c},{3,cc}], 5051: X =:= Y], 5052: {join,M}) 5053: end, 5054: R = qlc:e(F(nested_loop)), 5055: R = qlc:e(F(merge))">>, 5056: 5057: <<"F = fun(M) -> 5058: qlc:q([{XX,YY} || 5059: {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}], 5060: {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b},{2,bb},{2,bbb}, 5061: {4,d}], 5062: X =:= Y], 5063: {join,M}) 5064: end, 5065: R = qlc:e(F(nested_loop)), 5066: R = qlc:e(F(merge))">>, 5067: 5068: <<"Q = qlc:q([{XX,YY} || 5069: {XX,X} <- [{b,1},{c,3}], 5070: {Y,YY} <- [{1,a}], 5071: X =:= Y], 5072: {join,merge}), 5073: [{b,a}] = qlc:e(Q)">>, 5074: 5075: <<"Q = qlc:q([{XX,YY} || 5076: {XX,X} <- [{b,1},{c,3}], 5077: {Y,YY} <- qlc_SUITE:table_error([{1,a}], 1, err), 5078: X =:= Y], 5079: {join,merge}), 5080: err = qlc:e(Q)">>, 5081: 5082: <<"Q = qlc:q([{XX,YY} || 5083: {XX,X} <- [{a,1},{aa,1}], 5084: {Y,YY} <- [{1,a}], 5085: X =:= Y], 5086: {join,merge}), 5087: [{a,a},{aa,a}] = qlc:e(Q)">>, 5088: 5089: <<"Q = qlc:q([{XX,YY} || 5090: {XX,X} <- qlc_SUITE:table_error([{a,1},{aa,1}], 5091: 2, err), 5092: {Y,YY} <- [{1,a}], 5093: X =:= Y], 5094: {join,merge}), 5095: err = qlc:e(Q)">>, 5096: 5097: <<"Q = qlc:q([{XX,YY} || 5098: {XX,X} <- [{a,1}], 5099: {Y,YY} <- [{1,a},{1,aa}], 5100: X =:= Y], 5101: {join,merge}), 5102: [{a,a},{a,aa}]= qlc:e(Q)">>, 5103: 5104: <<"Q = qlc:q([{XX,YY} || 5105: {XX,X} <- qlc_SUITE:table_error([{a,1}], 2, err), 5106: {Y,YY} <- [{1,a},{1,aa}], 5107: X =:= Y], 5108: {join,merge}), 5109: C = qlc:cursor(Q), 5110: [{a,a}] = qlc:next_answers(C, 1), 5111: qlc:delete_cursor(C), 5112: err = qlc:e(Q)">>, 5113: 5114: <<"F = fun(M) -> 5115: qlc:q([{XX,YY} || 5116: {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}], 5117: {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b}, 5118: {2,bb},{2,bbb}], 5119: X =:= Y], 5120: {join,M}) 5121: end, 5122: %% [{a,a},{a,aa},{b,b},{b,bb},{b,bbb},{bb,b},{bb,bb},{bb,bbb}] 5123: R = qlc:e(F(nested_loop)), 5124: R = qlc:e(F(merge))">>, 5125: 5126: 5127: <<"F = fun(M) -> 5128: qlc:q([{XX,YY} || 5129: {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}], 5130: {Y,YY} <- qlc_SUITE:table_error([{0,a},{1,a},{1,aa}, 5131: {2,b},{2,bb}, 5132: {2,bbb}], 5133: 1, err), 5134: X =:= Y], 5135: {join,M}) 5136: end, 5137: %% [{a,a},{a,aa},{b,b},{b,bb},{b,bbb},{bb,b},{bb,bb},{bb,bbb}] 5138: err = qlc:e(F(nested_loop)), 5139: err = qlc:e(F(merge))">>, 5140: 5141: <<"Q = qlc:q([{XX,YY} || 5142: {XX,X} <- qlc_SUITE:table_error([], 2, err), 5143: {Y,YY} <- [{2,b},{3,c}], 5144: X =:= Y], 5145: {join,merge}), 5146: err = qlc:e(Q)">>, 5147: 5148: <<"Q = qlc:q([{XX,YY} || 5149: {XX,X} <- [{a,1},{c,3}], 5150: {Y,YY} <- [{2,b},{3,c}], 5151: X =:= Y], 5152: {join,merge}), 5153: [{c,c}] = qlc:e(Q)">>, 5154: 5155: <<"Q = qlc:q([{XX,YY} || 5156: {XX,X} <- [{a,1},{aa,1}], 5157: {Y,YY} <- [{1,a},{1,aa}], 5158: X =:= Y], 5159: {join,merge}), 5160: [{a,a},{a,aa},{aa,a},{aa,aa}] = qlc:e(Q)">>, 5161: 5162: <<"Q = qlc:q([{XX,YY} || 5163: {XX,X} <- [{a,1},{b,2}], 5164: {Y,YY} <- [{1,a},{1,aa}], 5165: X =:= Y], 5166: {join,merge}), 5167: [{a,a},{a,aa}] = qlc:e(Q)">>, 5168: 5169: <<"Q = qlc:q([{XX,YY} || 5170: {XX,X} <- [{a,1},{b,2}], 5171: {Y,YY} <- qlc_SUITE:table_error([{1,a},{1,aa}], 5172: 1, err), 5173: X =:= Y], 5174: {join,merge}), 5175: err = qlc:e(Q)">>, 5176: 5177: <<"Q = qlc:q([{XX,YY} || 5178: {XX,X} <- [{a,1},{b,2}], 5179: {Y,YY} <- [{1,a},{1,aa},{1,aaa},{1,aaaa}], 5180: X =:= Y], 5181: {join,merge}), 5182: [{a,a},{a,aa},{a,aaa},{a,aaaa}]= qlc:e(Q)">>, 5183: 5184: <<"Q = qlc:q([{element(1, X), element(2, Y)} || 5185: X <- [{a,1},{aa,1}], 5186: Y <- [{1,a},{1,aa}], 5187: element(2, X) =:= element(1, Y)], 5188: {join,merge}), 5189: [{a,a},{a,aa},{aa,a},{aa,aa}] = qlc:e(Q)">>, 5190: 5191: <<"Q = qlc:q([{element(1, X), element(2, Y)} || 5192: X <- [{a,1},{aa,1}], 5193: Y <- qlc_SUITE:table_error([], 1, err), 5194: element(2, X) =:= element(1, Y)], 5195: {join,merge}), 5196: err = qlc:e(Q)">>, 5197: 5198: <<"Q = qlc:q([{element(1, X), element(2, Y)} || 5199: X <- qlc_SUITE:table_error([{a,1}], 2, err), 5200: Y <- [{2,b}], 5201: element(2, X) =:= element(1, Y)], 5202: {join,merge}), 5203: err = qlc:e(Q)">>, 5204: 5205: <<"Q = qlc:q([{XX,YY} || 5206: {XX,X} <- [{1,a},{'1b',b},{2,b}], 5207: {Y,YY} <- [{a,1},{b,'1b'},{c,1}], 5208: X == Y], 5209: {join,merge}), 5210: [{1,1},{'1b','1b'},{2,'1b'}] = qlc:e(Q)">>, 5211: 5212: <<"Q = qlc:q([{XX,YY} || 5213: {XX,X} <- qlc_SUITE:table_error([{1,a},{'1b',b},{2,b}], 5214: 2, err), 5215: {Y,YY} <- [{a,1},{b,'1b'},{c,1}], 5216: X == Y], 5217: {join,merge}), 5218: err = qlc:e(Q)">> 5219: 5220: ], 5221: ?line run(Config, ETs), 5222: 5223: %% Mostly examples where temporary files are needed while merging. 5224: FTs = [ 5225: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)], 5226: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 5227: F = fun(J) -> 5228: qlc:q([{XX,YY} || 5229: {XX,X} <- L1, 5230: {Y,YY} <- L2, 5231: X == Y], 5232: {join,J}) 5233: end, 5234: Qm = F(merge), 5235: Qn = F(nested_loop), 5236: true = qlc:e(Qm,{max_list_size, 0}) =:= qlc:e(Qn)">>, 5237: 5238: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)], 5239: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 5240: Q = qlc:q([{XX,YY} || 5241: {XX,X} <- L1, 5242: {Y,YY} <- L2, 5243: X == Y], 5244: {join,merge}), 5245: {error,_,{file_error,_,_}} = 5246: qlc:e(Q, [{max_list_size,64*1024},{tmpdir,\"/a/b/c\"}])">>, 5247: 5248: <<"L1 = qlc_SUITE:table_error([{1,a},{2,a}], 2, err), 5249: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 5250: F = fun(J) -> 5251: qlc:q([{XX,YY} || 5252: {XX,X} <- L1, 5253: {Y,YY} <- L2, 5254: X == Y], 5255: {join,J}) 5256: end, 5257: Qm = F(merge), 5258: Qn = F(nested_loop), 5259: err = qlc:e(Qm, {max_list_size,64*1024}), 5260: err = qlc:e(Qn)">>, 5261: 5262: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)], 5263: L2 = qlc_SUITE:table_error([{a,Y} || Y <- lists:seq(1, 10000)], 5264: 1, err), 5265: F = fun(J) -> 5266: qlc:q([{XX,YY} || 5267: {XX,X} <- L1, 5268: {Y,YY} <- L2, 5269: X == Y], 5270: {join,J}) 5271: end, 5272: Qm = F(merge), 5273: Qn = F(nested_loop), 5274: err = qlc:e(Qm, {max_list_size,64*1024}), 5275: err = qlc:e(Qn)">>, 5276: 5277: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)] ++ 5278: [{'1b',b},{2,b}] ++ [{Y,d} || Y <- lists:seq(1, 2)], 5279: L2 = [{a,Y} || Y <- lists:seq(1, 10000)] ++ 5280: [{b,'1b'}] ++ [{c,1}] ++ [{d,Y} || Y <- lists:seq(1, 10000)], 5281: F = fun(J) -> 5282: qlc:q([{XX,YY} || 5283: {XX,X} <- L1, 5284: {Y,YY} <- L2, 5285: X == Y], 5286: {join,J}) 5287: end, 5288: Qm = F(merge), 5289: Qn = F(nested_loop), 5290: true = lists:sort(qlc:e(Qm, {max_list_size,64*1024})) =:= 5291: lists:sort(qlc:e(Qn))">>, 5292: 5293: <<"F = fun(J) -> 5294: qlc:q([{XX,YY} || 5295: {XX,X} <- [{Y,a} || Y <- lists:seq(1, 2)], 5296: {Y,YY} <- [{a,Y} || Y <- lists:seq(1,100000)], 5297: X == Y], 5298: {join,J}) 5299: end, 5300: Qm = F(merge), 5301: Qn = F(nested_loop), 5302: true = qlc:e(Qm, {max_list_size,64*1024}) =:= qlc:e(Qn)">>, 5303: 5304: %% More than one join in one QLC expression. 5305: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)], 5306: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 5307: F = fun(J) -> 5308: Q = qlc:q([{XX,YY} || 5309: {XX,X} <- L1, 5310: {Y,YY} <- L2, 5311: X == Y, 5312: begin XX > 1 end, 5313: begin YY > 9999 end], 5314: {join,J}), 5315: qlc:q([{XX1,YY1,XX2,YY2} || 5316: {XX1,YY1} <- Q, 5317: {XX2,YY2} <- Q]) 5318: end, 5319: Qm = F(merge), 5320: Qn = F(nested_loop), 5321: R1 = lists:sort(qlc:e(Qm, {max_list_size,64*1024})), 5322: R2 = lists:sort(qlc:e(Qm, {max_list_size,1 bsl 31})), 5323: true = R1 =:= lists:sort(qlc:e(Qn)), 5324: true = R1 =:= R2">>, 5325: 5326: <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)], 5327: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 5328: F = fun(J) -> 5329: Q = qlc:q([{XX,YY} || 5330: {XX,X} <- L1, 5331: {Y,YY} <- L2, 5332: X == Y, 5333: begin XX > 1 end, 5334: begin YY > 9999 end], 5335: {join,J}), 5336: qlc:q([{XX1,YY1,XX2,YY2} || 5337: {XX1,YY1} <- Q, 5338: {XX2,YY2} <- Q, 5339: throw(thrown)]) 5340: end, 5341: Qm = F(merge), 5342: thrown = (catch {any_term, qlc:e(Qm, {max_list_size,64*1024})})">>, 5343: 5344: <<"%% Bigger than 64*1024. 5345: T1 = {1, lists:seq(1, 20000)}, 5346: L1 = [{a,T1},{b,T1}], 5347: L2 = [{T1,a},{T1,b}], 5348: F = fun(J) -> 5349: qlc:q([{XX,YY} || 5350: {XX,X} <- L1, 5351: {Y,YY} <- L2, 5352: X == Y], 5353: {join,J}) 5354: end, 5355: Qm = F(merge), 5356: Qn = F(nested_loop), 5357: R = [{a,a},{a,b},{b,a},{b,b}], 5358: R = qlc:e(Qm, {max_list_size,64*1024}), 5359: R = qlc:e(Qn)">>, 5360: 5361: <<"%% Bigger than 64*1024. No temporary files. 5362: T1 = {1, lists:seq(1, 20000)}, 5363: L1 = [{a,T1},{b,T1}], 5364: L2 = [{T1,a},{T1,b}], 5365: F = fun(J) -> 5366: qlc:q([{XX,YY} || 5367: {XX,X} <- L1, 5368: {Y,YY} <- L2, 5369: X == Y], 5370: {join,J}) 5371: end, 5372: Qm = F(merge), 5373: Qn = F(nested_loop), 5374: R = [{a,a},{a,b},{b,a},{b,b}], 5375: R = qlc:e(Qm, {max_list_size,1 bsl 31}), 5376: R = qlc:e(Qn)">> 5377: 5378: 5379: ], 5380: ?line run(Config, FTs), 5381: 5382: ok. 5383: 5384: join_sort(doc) -> 5385: "Merge join optimizations (avoid unnecessary sorting)."; 5386: join_sort(suite) -> []; 5387: join_sort(Config) when is_list(Config) -> 5388: Ts = [ 5389: <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}]), 5390: H1 = qlc:q([X || X <- H1_1], unique), 5391: H2 = qlc:keysort(2, [{1,2},{3,4}]), 5392: H3 = qlc:q([{X,Y} || {X,_,_} <- H1, 5393: {_,Y} <- H2, 5394: X =:= Y]), 5395: {1,0,0,2} = join_info(H3), 5396: [{4,4}] = qlc:e(H3)">>, 5397: 5398: <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}]), 5399: H1 = qlc:q([X || X <- H1_1], unique), % keeps the order 5400: H2 = qlc:keysort(2, [{1,2},{3,4}]), 5401: H3 = qlc:q([{X,Y} || {X,_,_} <- H1, % no extra keysort 5402: {Y,_} <- H2, % an extra keysort 5403: X =:= Y]), 5404: {1,0,0,3} = join_info(H3), 5405: [{1,1}] = qlc:e(H3)">>, 5406: 5407: <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}], {tmpdir,\"\"}), 5408: H1 = qlc:q([X || X <- H1_1], unique), 5409: H2 = qlc:keysort(2, [{1,2},{3,4}]), 5410: H3 = qlc:q([{X,Y} || {_,X,_} <- H1, 5411: {_,Y} <- H2, 5412: X =:= Y]), 5413: {1,0,0,3} = join_info(H3), 5414: [{2,2}] = qlc:e(H3)">>, 5415: 5416: <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}], {tmpdir,\"\"}), 5417: H1 = qlc:q([X || X <- H1_1], unique), 5418: H2 = qlc:keysort(2, [{1,2},{3,4}]), 5419: H3 = qlc:q([{X,Y} || {_,X,_} <- H1, 5420: {_,Y} <- H2, 5421: X =:= Y]), 5422: {1,0,0,3} = join_info(H3), 5423: [{2,2}] = qlc:e(H3)">>, 5424: 5425: <<"H1 = qlc:sort([{1,a},{2,b},{3,c}]), 5426: %% Since H1 is sorted it is also keysorted on the first column. 5427: Q = qlc:q([{X, Y} || {X,_} <- H1, 5428: {Y} <- [{0},{1},{2}], 5429: X == Y]), 5430: {1,0,0,1} = join_info(Q), 5431: [{1,1},{2,2}] = qlc:e(Q)">>, 5432: 5433: <<"H1 = qlc:sort([{r,a,1},{r,b,2},{r,c,3}]), 5434: Q = qlc:q([{X, Y} || {r,_,X} <- H1, % needs keysort(3) 5435: {Y} <- [{0},{1},{2}], 5436: X == Y]), 5437: {1,0,0,2} = join_info(Q), 5438: [{1,1},{2,2}] = qlc:e(Q)">>, 5439: 5440: <<"QH = qlc:q([X || X <- [{1,2,3},{4,5,6}], 5441: Y <- qlc:sort([{1,2},{3,4}]), 5442: element(1, X) =:= element(2, Y)]), 5443: {1,0,0,2} = join_info_count(QH), 5444: [{4,5,6}] = qlc:e(QH)">>, 5445: 5446: <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6},{1,2,3}]), 5447: H1 = qlc:q([X || X <- H1_1], unique), 5448: H2 = qlc:keysort(2, [{2,1},{3,4}]), 5449: H3 = qlc:q([{X,Y} || {X,_,_} <- H1, 5450: {_,Y} <- H2, 5451: X =:= Y]), 5452: H4 = qlc:keysort(1, [{1,2},{3,4},{4,a}]), 5453: H5 = qlc:q([{X,Y} || {X,_} <- H4, 5454: {_,Y} <- H3, 5455: X =:= Y]), 5456: {2,0,0,3} = join_info_count(H5), 5457: [{1,1},{4,4}]= qlc:e(H5)">>, 5458: 5459: <<" 5460: H1 = qlc:keysort(2, [{1,a,u},{2,b,k},{3,c,l}]), 5461: H2 = qlc:q([{a,X,Y,a} || {1,X,u} <- H1, 5462: {2,Y,k} <- H1]), 5463: %% Neither H1 nor H2 need to be key-sorted 5464: %% (the columns are constant). 5465: H3 = qlc:q([{A,B,C,D,E,F,G,H} || 5466: {A,B,C,D} <- H2, 5467: {E,F,G,H} <- H2, 5468: A =:= H], 5469: {join,merge}), 5470: {1,0,0,4} = join_info_count(H3), 5471: [{a,a,b,a,a,a,b,a}] = qlc:e(H3)">>, 5472: 5473: <<"%% Q1 is sorted on X or Y. 5474: Q1 = qlc:q([{X,Y} || 5475: {X,_} <- qlc:keysort(1, [{1,a},{2,b}]), 5476: {_,Y} <- qlc:keysort(2, [{aa,11},{bb,22}]), 5477: X < Y]), 5478: [{1,11},{1,22},{2,11},{2,22}] = qlc:e(Q1), 5479: Q = qlc:q([{X,Y} || 5480: {X,_} <- Q1, % no need to sort Q1 5481: {Y} <- [{0},{1},{2},{3}], 5482: X =:= Y]), 5483: {1,0,0,3} = join_info_count(Q), 5484: [{1,1},{1,1},{2,2},{2,2}] = qlc:e(Q)">>, 5485: 5486: <<"H1 = qlc:keysort([2], [{r,1},{r,2},{r,3}]), 5487: %% H1 is actually sorted, but this info is not captured. 5488: Q = qlc:q([{X, Y} || {r,X} <- H1, 5489: {Y} <- [{0},{1},{2}], 5490: X == Y]), 5491: {1,0,0,2} = join_info_count(Q), 5492: [{1,1},{2,2}] = qlc:e(Q)">>, 5493: 5494: <<"%% Two leading constants columns and sorted objects 5495: %% implies keysorted on column 3. 5496: H1 = qlc:sort(qlc:q([{a,X,Y} || {X,Y} <- [{1,2},{2,3},{3,3}]])), 5497: H2 = qlc:q([{X,Y} || 5498: {a,3,X} <- H1, 5499: {a,2,Y} <- H1, 5500: X =:= Y]), 5501: {1,0,0,0} = join_info_count(H2), 5502: [{3,3}] = qlc:e(H2)">>, 5503: 5504: <<"QH = qlc:q([{X,Y} || {X,Y} <- [{1,4},{1,3}], 5505: {Z} <- [{1}], 5506: X =:= Z, (Y =:= 3) or (Y =:= 4)]), 5507: {1,0,0,1} = join_info_count(QH), 5508: [{1,4},{1,3}] = qlc:e(QH)">>, 5509: 5510: <<"E = ets:new(join, [ordered_set]), 5511: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 5512: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E), % no need to sort 5513: {Y} <- [{0},{1},{2}], 5514: X == Y], {join,merge}), 5515: {1,0,0,1} = join_info(Q), 5516: [{1,1},{2,2}] = qlc:e(Q), 5517: ets:delete(E)">>, 5518: 5519: <<"H1 = qlc:sort([{r,1,a},{r,2,b},{r,3,c}]), 5520: Q = qlc:q([{X, Y} || {r,X,_} <- H1, % does not need keysort(3) 5521: {Y} <- [{0},{1},{2}], 5522: X == Y]), 5523: {1,0,0,1} = join_info(Q), 5524: [{1,1},{2,2}] = qlc:e(Q)">>, 5525: 5526: <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]), 5527: H2 = [{a},{b}], 5528: %% Several columns in different qualifiers have initial 5529: %% constant columns. 5530: H3 = qlc:keysort(1,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]), 5531: Q = qlc:q([{r,X,Y,Z} || {r,X} <- H1, 5532: {Y} <- H2, 5533: {c1,c2,Z} <- H3, 5534: X =:= Z], {join,merge}), 5535: {1,0,0,3} = join_info(Q), 5536: [{r,1,a,1},{r,1,b,1},{r,2,a,2},{r,2,b,2},{r,3,a,3},{r,3,b,3}] = 5537: qlc:e(Q)">>, 5538: 5539: <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]), 5540: H2 = [{a},{b}], 5541: %% As the last one, but one keysort less. 5542: H3 = qlc:keysort(3,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]), 5543: Q = qlc:q([{r,X,Y,Z} || {r,X} <- H1, 5544: {Y} <- H2, 5545: {c1,c2,Z} <- H3, 5546: X =:= Z], {join,merge}), 5547: {1,0,0,2} = join_info(Q), 5548: [{r,1,a,1},{r,1,b,1},{r,2,a,2},{r,2,b,2},{r,3,a,3},{r,3,b,3}] = 5549: qlc:e(Q)">>, 5550: 5551: <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]), 5552: H2 = [{a},{b}], 5553: H3 = qlc:keysort(1,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]), 5554: %% One generator before the joined generators. 5555: Q = qlc:q([{r,X,Y,Z} || {Y} <- H2, 5556: {r,X} <- H1, 5557: {c1,c2,Z} <- H3, 5558: X =:= Z], {join,merge}), 5559: {1,0,0,3} = join_info(Q), 5560: [{r,1,a,1},{r,2,a,2},{r,3,a,3},{r,1,b,1},{r,2,b,2},{r,3,b,3}] = 5561: qlc:e(Q)">>, 5562: 5563: <<"H1 = [{a,1},{b,2},{c,3},{d,4}], 5564: H2 = [{a},{b}], 5565: H3 = [{c1,c2,a},{foo,bar,b},{c1,c2,c},{c1,c2,d}], 5566: %% A couple of \"extra\" filters and generators. 5567: Q = qlc:q([{X,Y,Z} || {X,_} <- H1, 5568: {Y} <- H2, 5569: X > Y, 5570: {c1,c2,Z} <- H3, 5571: {W} <- [{a},{b}], 5572: W > a, 5573: X =:= Z]), 5574: {1,0,0,2} = join_info(Q), 5575: [{c,a,c},{c,b,c},{d,a,d},{d,b,d}] = qlc:e(Q)">>, 5576: 5577: <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]), 5578: H2 = qlc:sort([{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]), 5579: %% H2 is sorted, no keysort necessary. 5580: %% This example shows that the 'filter-part' of the pattern 5581: %% ({c1,c2,Z}) should be evaluated _before_ the join. 5582: %% Otherwise the objects cannot be assumed to be keysort:ed on the 5583: %% third column (if merge join), and lookup-join would lookup 5584: %% more keys than necessary. 5585: Q = qlc:q([{r,X,Z} || {r,X} <- H1, 5586: {c1,c2,Z} <- H2, 5587: X =:= Z] ,{join,merge}), 5588: {1,0,0,1} = join_info(Q), 5589: [{r,1,1},{r,2,2},{r,3,3}] = qlc:e(Q)">>, 5590: 5591: <<"H1 = [{1,a},{2,b},{3,c}], 5592: H2 = [{0,0},{1,1},{2,2}], 5593: H3 = qlc:q([{A,C,D} || 5594: {A,_B} <- H1, 5595: {C,D} <- H2, 5596: A == D, C == D]), 5597: H4 = [{1,1},{2,2},{3,3}], 5598: H5 = qlc:q([{X,Y} || 5599: {X,_,_} <- H3, % no need to sort this one (merge join) 5600: {_,Y} <- H4, 5601: X == Y]), 5602: Q = qlc:q([{X,Y} || 5603: {X,_} <- H5, % no need to sort this one 5604: {Y,_} <- H4, 5605: X == Y]), 5606: {{3,0,0,4},{3,0,0,6}} = join_info(Q), 5607: [{1,1},{2,2}] = qlc:e(Q)">>, 5608: 5609: <<"%% There is an extra test (_C1, element(1, X) =:= 1) that is not 5610: %% necessary since the match spec does the same check. This can be 5611: %% improved upon. 5612: Q = qlc:q([{X,Y} || 5613: X <- [{2},{1}], 5614: element(1, X) =:= 1, 5615: Y=_ <- [{2},{1}], 5616: element(1, X) =:= element(1, Y)]), 5617: {qlc,_, 5618: [{generate,_,{qlc,_, 5619: [{generate,_,{qlc,_, 5620: [{generate,_,{list,{list,_},_}}, 5621: _C1],[]}}, 5622: {generate,_,{qlc,_, 5623: [{generate,_,{list,[{2},{1}]}}, 5624: _C2],[]}},_], 5625: [{join,merge}]}},_],[]} = i(Q), 5626: {1,0,0,0} = join_info_count(Q), 5627: [{{1},{1}}] = qlc:e(Q)">>, 5628: 5629: <<"etsc(fun(E) -> 5630: L = [{a,b,a},{c,d,b},{1,2,a},{3,4,b}], 5631: Q = qlc:q([P1 || {X,2,Z}=P1 <- ets:table(E), 5632: Y <- L, 5633: X =:= 1, 5634: Z =:= a, 5635: P1 =:= Y, 5636: X =:= element(1, Y)]), 5637: {1,0,0,0} = join_info_count(Q), 5638: [{1,2,a}] = qlc:e(Q) 5639: end, [{1,2,a},{3,4,b}])">>, 5640: 5641: %% Merge join on Z and element(3, Y). No need to sort! 5642: <<"etsc(fun(E) -> 5643: L = [{a,b,a},{c,d,b},{1,2,a},{3,4,b}], 5644: Q = qlc:q([P1 || {X,2,Z}=P1 <- ets:table(E), 5645: Y <- L, 5646: (X =:= 1) or (X =:= 2), 5647: Z =:= a, 5648: P1 =:= Y, 5649: X =:= element(1, Y)]), 5650: {1,0,0,0} = join_info_count(Q), 5651: [{1,2,a}] = qlc:e(Q) 5652: end, [{1,2,a},{3,4,b}])">>, 5653: 5654: <<"%% Y is constant as well as X. No keysort, which means that 5655: %% Y must be filtered before merge join. 5656: etsc(fun(E) -> 5657: Q = qlc:q([X || {1,2}=X <- ets:table(E), 5658: Y <- [{a,b},{c,d},{1,2},{3,4}], 5659: X =:= Y, 5660: element(1, X) =:= element(1, Y)]), 5661: {1,0,0,0} = join_info_count(Q), 5662: [{1,2}] = qlc:e(Q) 5663: end, [{1,2},{3,4}])">> 5664: 5665: ], 5666: ?line run(Config, Ts), 5667: ok. 5668: 5669: join_complex(doc) -> 5670: "Join of more than two columns."; 5671: join_complex(suite) -> []; 5672: join_complex(Config) when is_list(Config) -> 5673: Ts = [{three, 5674: <<"three() -> 5675: L = [], 5676: Q = qlc:q([{X,Y,Z} || {X,_} <- L, 5677: {_,Y} <- L, 5678: {Z,_} <- L, 5679: X =:= Y, Y == Z 5680: ]), 5681: qlc:e(Q).">>, 5682: [], 5683: {warnings,[{3,qlc,too_complex_join}]}}, 5684: 5685: {two, 5686: <<"two() -> 5687: Q = qlc:q([{X,Y,Z,W} || 5688: {X} <- [], 5689: {Y} <- [], 5690: {Z} <- [], 5691: {W} <- [], 5692: X =:= Y, 5693: Z =:= W],{join,merge}), 5694: qlc:e(Q).">>, 5695: [], 5696: {warnings,[{2,qlc,too_many_joins}]}} 5697: ], 5698: 5699: ?line compile(Config, Ts), 5700: 5701: Ts2 = [{three, 5702: <<"three() -> 5703: L = [], 5704: Q = qlc:q([{X,Y,Z} || {X,_} <- L, 5705: {_,Y} <- L, 5706: {Z,_} <- L, 5707: X =:= Y, Y == Z 5708: ]), 5709: qlc:e(Q).">>, 5710: [], 5711: {[], 5712: ["cannot handle join of three or more generators efficiently"]}}, 5713: 5714: {two, 5715: <<"two() -> 5716: Q = qlc:q([{X,Y,Z,W} || 5717: {X} <- [], 5718: {Y} <- [], 5719: {Z} <- [], 5720: {W} <- [], 5721: X =:= Y, 5722: Z =:= W],{join,merge}), 5723: qlc:e(Q).">>, 5724: [], 5725: {[],["cannot handle more than one join efficiently"]}} 5726: ], 5727: 5728: ?line compile_format(Config, Ts2), 5729: 5730: ok. 5731: 5732: 5733: otp_5644(doc) -> 5734: "OTP-5644. Handle the new language element M:F/A."; 5735: otp_5644(suite) -> []; 5736: otp_5644(Config) when is_list(Config) -> 5737: Ts = [ 5738: <<"Q = qlc:q([fun modul:mfa/0 || _ <- [1,2], 5739: is_function(fun modul:mfa/0, 0)]), 5740: [_,_] = qlc:eval(Q)">> 5741: ], 5742: 5743: ?line run(Config, Ts), 5744: ok. 5745: 5746: otp_5195(doc) -> 5747: "OTP-5195. Allow traverse functions returning terms."; 5748: otp_5195(suite) -> []; 5749: otp_5195(Config) when is_list(Config) -> 5750: %% Several minor improvements have been implemented in OTP-5195. 5751: %% The test cases are spread all over... except these. 5752: %% 5753: %% Traverse functions returning terms. 5754: 5755: Ts = [<<"L = [1,2,3], 5756: Err = {error,modul,err}, 5757: H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]), 5758: Err = qlc:e(H)">>, 5759: 5760: <<"Err = {error,modul,err}, 5761: TravFun = fun() -> Err end, 5762: H1 = qlc:sort(qlc:q([X || X <- qlc:table(TravFun, [])])), 5763: H = qlc:q([{X} || X <- H1]), 5764: Err = qlc:e(H)">>, 5765: 5766: <<"L = [1,2,3], 5767: Err = {error,modul,err}, 5768: H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]), 5769: C = qlc:cursor(H), 5770: R = qlc:next_answers(C, all_remaining), 5771: qlc:delete_cursor(C), 5772: Err = R">>, 5773: 5774: <<"L = [1,2,3], 5775: Err = {error,modul,err}, 5776: H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]), 5777: F = fun(Obj, A) -> A++[Obj] end, 5778: Err = qlc:fold(F, [], H)">>, 5779: 5780: <<"Err = {error,modul,err}, 5781: TravFun = fun() -> Err end, 5782: H1 = qlc:sort(qlc:q([X || X <- qlc:table(TravFun, [])])), 5783: H = qlc:q([{X} || X <- H1]), 5784: F = fun(Obj, A) -> A++[Obj] end, 5785: Err = qlc:fold(F, [], H)">>, 5786: 5787: <<"Q1 = qlc:append([qlc:append([ugly()]),[3]]), 5788: Q = qlc:q([X || X <- Q1]), 5789: 42 = qlc:e(Q), 5790: ok. 5791: 5792: ugly() -> 5793: [apa | fun() -> 42 end]. 5794: foo() -> bar">>, 5795: 5796: <<"L = [1,2,3], 5797: Err = {error,modul,err}, 5798: H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]), 5799: H1 = qlc:q([X || X <- H], unique), 5800: Err = qlc:e(H1)">>, 5801: 5802: <<"Err = {error, module, err}, 5803: L = [1,2,3], 5804: H1 = qlc:q([{X} || X <- qlc_SUITE:table_error(L, Err)]), 5805: H = qlc:q([{X,Y,Z} || X <- H1, Y <- H1, Z <- L], cache), 5806: qlc:e(H, cache_all)">>, 5807: 5808: <<"Err = {error, module, err}, 5809: L = [1,2,3], 5810: H1 = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]), 5811: H = qlc:q([{X,Y,Z} || X <- H1, Y <- H1, Z <- L], cache), 5812: qlc:e(H, [cache_all,unique_all])">>, 5813: 5814: <<"L = [{1},{2},{3}], 5815: H = qlc:q([X || {X} <- qlc_SUITE:table_lookup_error(L), 5816: X =:= 2]), 5817: {error, lookup, failed} = qlc:e(H)">>, 5818: 5819: %% The traverse function can return any value, but it must not 5820: %% return an improper list. Improper lists must not be given anyway. 5821: <<"{'EXIT', {{badfun,a},_}} = 5822: (catch qlc:e(qlc:q([{X} || X <- [1 | a], begin true end])))">> 5823: 5824: ], 5825: 5826: ?line run(Config, Ts), 5827: 5828: Ts2 = [<<"Q = qlc:q([{X,Y} || {X} <- [{1},{2},{3}], 5829: begin 5830: %% Used to generate a badly formed file 5831: Y = 3, true 5832: end, 5833: X =:= Y]), 5834: [{3,3}] = qlc:e(Q)">>], 5835: ?line run(Config, Ts2), 5836: 5837: ok. 5838: 5839: otp_6038_bug(doc) -> 5840: "OTP-6038. Bug fixes: unique and keysort; cache."; 5841: otp_6038_bug(suite) -> []; 5842: otp_6038_bug(Config) when is_list(Config) -> 5843: %% The 'unique' option can no longer be merged with the keysort options. 5844: %% This used to return [{1,a},{1,c},{2,b},{2,d}], but since 5845: %% file_sorter:keysort now removes duplicates based on keys, the 5846: %% correct return value is [{1,a},{2,b}]. 5847: Ts = [<<"H1 = qlc:q([X || X <- [{1,a},{2,b},{1,c},{2,d}]], unique), 5848: H2 = qlc:keysort(1, H1, [{unique,true}]), 5849: [{1,a},{2,b}] = qlc:e(H2)">>], 5850: 5851: ?line run(Config, Ts), 5852: 5853: %% Sometimes the cache options did not empty the correct tables. 5854: CTs = [ 5855: <<"Options = [cache,unique], 5856: V1 = qlc:q([{X,Y} || X <- [1,2], Y <- [3]], Options), 5857: V2 = qlc:q([{X,Y} || X <- [a,b], Y <- V1]), 5858: V3 = qlc:q([{X,Y} || X <- [5,6], Y <- [7]], Options), 5859: Q = qlc:q([{X,Y} || X <- V2, Y <- V3]), 5860: R = qlc:e(Q), 5861: L1 = [{X,Y} || X <- [1,2], Y <- [3]], 5862: L2 = [{X,Y} || X <- [a,b], Y <- L1], 5863: L3 = [{X,Y} || X <- [5,6], Y <- [7]], 5864: L = [{X,Y} || X <- L2, Y <- L3], 5865: true = R =:= L">>, 5866: <<"Options = [cache,unique], 5867: V1 = qlc:q([{X,Y} || X <- [1,2], Y <- [3]], Options), 5868: V2 = qlc:q([{X,Y} || X <- [a,b], Y <- V1]), 5869: V3 = qlc:q([{X,Y} || X <- [5,6], Y <- [7]], Options), 5870: V4 = qlc:q([{X,Y} || X <- V2, Y <- V3], Options), 5871: Q = qlc:q([{X,Y} || X <- [1,2], Y <- V4]), 5872: R = qlc:e(Q), 5873: L1 = [{X,Y} || X <- [1,2], Y <- [3]], 5874: L2 = [{X,Y} || X <- [a,b], Y <- L1], 5875: L3 = [{X,Y} || X <- [5,6], Y <- [7]], 5876: L4 = [{X,Y} || X <- L2, Y <- L3], 5877: L = [{X,Y} || X <- [1,2], Y <- L4], 5878: true = R =:= L">> 5879: ], 5880: ?line run(Config, CTs), 5881: 5882: ok. 5883: 5884: otp_6359(doc) -> 5885: "OTP-6359. dets:select() never returns the empty list."; 5886: otp_6359(suite) -> []; 5887: otp_6359(Config) when is_list(Config) -> 5888: dets:start(), 5889: T = luna, 5890: Fname = filename(T, Config), 5891: 5892: Ts = [ 5893: [<<"T = luna, Fname = \"">>, Fname, <<"\", 5894: {ok, _} = dets:open_file(T, [{file,Fname}]), 5895: Q = qlc:q([F || 5896: F <- dets:table(T), 5897: (F band ((1 bsl 0)) =/= 0), 5898: true]), 5899: [] = qlc:eval(Q), 5900: ok = dets:close(T), 5901: file:delete(\"">>, Fname, <<"\"), 5902: ok">>] 5903: ], 5904: 5905: ?line run(Config, Ts), 5906: ok. 5907: 5908: otp_6562(doc) -> 5909: "OTP-6562. compressed = false (should be []) when sorting before join."; 5910: otp_6562(suite) -> []; 5911: otp_6562(Config) when is_list(Config) -> 5912: Bug = [ 5913: %% This example uses a file to sort E2 on the second column. It is 5914: %% not easy to verify that this happens; the file_sorter module's 5915: %% size option cannot be set in this case. But it is not likely 5916: %% that the default size (512 KiB) will ever change, so it should 5917: %% be future safe. 5918: <<"E1 = create_ets(1, 10), 5919: E2 = create_ets(5, 150000), 5920: Q = qlc:q([{XX,YY} || 5921: {X,XX} <- ets:table(E1), 5922: {YY,Y} <- ets:table(E2), 5923: X == Y], 5924: {join,merge}), 5925: [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = qlc:e(Q), 5926: ets:delete(E1), 5927: ets:delete(E2)">> 5928: ], 5929: ?line run(Config, Bug), 5930: 5931: Bits = [ 5932: {otp_6562_1, 5933: <<"otp_6562_1() -> 5934: Q = qlc:q([X || <<X:8>> <= <<\"hej\">>]), 5935: qlc:info(Q). 5936: ">>, 5937: [], 5938: {errors,[{2,qlc,binary_generator}], 5939: []}} 5940: ], 5941: ?line [] = compile(Config, Bits), 5942: 5943: ?line R1 = {error,qlc,{1,qlc,binary_generator}} 5944: = qlc:string_to_handle("[X || <<X:8>> <= <<\"hej\">>]."), 5945: ?line "1: cannot handle binary generators\n" = 5946: lists:flatten(qlc:format_error(R1)), 5947: 5948: ok. 5949: 5950: otp_6590(doc) -> 5951: "OTP-6590. Bug fix (join info)."; 5952: otp_6590(suite) -> []; 5953: otp_6590(Config) when is_list(Config) -> 5954: Ts = [<<"fun(Tab1Value) -> 5955: Q = qlc:q([T1#tab1.id || T1 <- [#tab1{id = id1, 5956: value = v, 5957: tab2_id = id}], 5958: T2 <- [#tab2{id = id}], 5959: T1#tab1.value =:= Tab1Value, 5960: T1#tab1.tab2_id =:= T2#tab2.id]), 5961: [id1] = qlc:e(Q) 5962: end(v)">>], 5963: 5964: ?line run(Config, <<"-record(tab1, {id, tab2_id, value}). 5965: -record(tab2, {id, value}).\n">>, Ts), 5966: ok. 5967: 5968: otp_6673(doc) -> 5969: "OTP-6673. Optimizations and fixes."; 5970: otp_6673(suite) -> []; 5971: otp_6673(Config) when is_list(Config) -> 5972: Ts_PT = 5973: [<<"etsc(fun(E1) -> 5974: etsc(fun(E2) -> 5975: Q = qlc:q([{A,B,C,D} || 5976: {A,B} <- ets:table(E1), 5977: {C,D} <- ets:table(E2), 5978: A =:= 2, % lookup 5979: B =:= D, % join 5980: C =:= g]), % lookup invalidated by join 5981: {qlc,_,[{generate,_, 5982: {qlc,_, 5983: [{generate,_, 5984: {qlc,_,[{generate,_, 5985: {keysort, 5986: {list,{table,_}, 5987: [{{'$1','$2'},[],['$_']}]}, 5988: 2,[]}},_],[]}}, 5989: {generate,_,{qlc,_, 5990: [{generate,_, 5991: {keysort,{table,_},2,[]}}], 5992: []}},_], 5993: [{join,merge}]}},_,_],[]} = i(Q), 5994: [{2,y,g,y}] = qlc:e(Q) 5995: end, [{f,x},{g,y},{h,z}]) 5996: end, 5997: [{1,x},{2,y},{3,z}])">>, 5998: <<"etsc(fun(E1) -> 5999: etsc(fun(E2) -> 6000: Q = qlc:q([{A,B,C,D} || 6001: {A,B} <- ets:table(E1), 6002: {C,D} <- ets:table(E2), 6003: A =:= 2, % lookup 6004: C =:= g, % lookup 6005: B =:= D]), % join 6006: {qlc,_,[{generate,_, 6007: {qlc,_, 6008: [{generate,_, 6009: {qlc,_,[{generate,_, 6010: {keysort, 6011: {list,{table,_}, 6012: [{{'$1','$2'},[],['$_']}]}, 6013: 2,[]}},_],[]}}, 6014: {generate,_,{qlc,_, 6015: [{generate,_, 6016: {keysort, 6017: {list,{table,_}, 6018: [{{'$1','$2'},[],['$_']}]}, 6019: 2,[]}},_],[]}},_], 6020: [{join,merge}]}},_],[]} = i(Q), 6021: [{2,y,g,y}] = qlc:e(Q) 6022: end, [{f,x},{g,y},{h,z}]) 6023: end, 6024: [{1,x},{2,y},{3,z}])">>], 6025: 6026: ?line run(Config, Ts_PT), 6027: 6028: MS = ets:fun2ms(fun({X,_Y}=T) when X > 1 -> T end), 6029: Ts_RT = [ 6030: [<<"%% Explicit match-spec. ets:table() ensures there is no lookup 6031: %% function, which means that lookup join will not be considered. 6032: MS = ">>, io_lib:format("~w", [MS]), <<", 6033: etsc(fun(E) -> 6034: F = fun(J) -> 6035: qlc:q([{X,W} || 6036: {X,_Y} <- 6037: ets:table(E,{traverse, 6038: {select,MS}}), 6039: {Z,W} <- [{1,1},{2,2},{3,3}], 6040: X =:= Z], {join,J}) 6041: end, 6042: Qm = F(any), 6043: [{2,2},{3,3}] = qlc:e(Qm), 6044: {'EXIT',{cannot_carry_out_join,_}} = 6045: (catch qlc:e(F(lookup))) 6046: end, [{1,a},{2,b},{3,c}])">>], 6047: 6048: <<"%% The filter 'A =< y' can be evaluated by traversing E1 using a 6049: %% match specification, but then lookup join cannot use E1 for 6050: %% looking up keys. This example shows that the filter is kept if 6051: %% lookup join is employed (otherwise it is optimized away since 6052: %% the match spec is used). 6053: etsc(fun(E1) -> 6054: Q = qlc:q([{A,B,C,D} || 6055: {A,B} <- ets:table(E1), 6056: {C,D} <- [{x,f},{y,g},{z,h}], 6057: A =< y, % kept 6058: A =:= C], {join,lookup}), 6059: [{x,1,x,f},{y,2,y,g}] = lists:sort(qlc:e(Q)) 6060: end, [{x,1},{y,2},{z,3}])">> 6061: 6062: ], 6063: ?line run(Config, Ts_RT), 6064: 6065: ok. 6066: 6067: otp_6964(doc) -> 6068: "OTP-6964. New option 'tmpdir_usage'."; 6069: otp_6964(suite) -> []; 6070: otp_6964(Config) when is_list(Config) -> 6071: T1 = [ 6072: <<"Q1 = qlc:q([{X} || X <- [1,2]]), 6073: {'EXIT', {badarg,_}} = (catch qlc:e(Q1, {tmpdir_usage,bad})), 6074: %% merge join 6075: F = fun(Use) -> 6076: L1 = [{Y,a} || Y <- lists:seq(1, 2)], 6077: L2 = [{a,Y} || Y <- lists:seq(1, 10000)], 6078: Q = qlc:q([{XX,YY} || 6079: {XX,X} <- L1, 6080: {Y,YY} <- L2, 6081: X == Y], 6082: {join,merge}), 6083: qlc:e(Q, [{max_list_size,64*1024},{tmpdir_usage,Use}]) 6084: end, 6085: D = erlang:system_flag(backtrace_depth, 0), 6086: try 6087: 20000 = length(F(allowed)), 6088: ErrReply = F(not_allowed), 6089: {error, qlc, {tmpdir_usage,joining}} = ErrReply, 6090: \"temporary file was needed for joining\n\" = 6091: lists:flatten(qlc:format_error(ErrReply)), 6092: qlc_SUITE:install_error_logger(), 6093: 20000 = length(F(warning_msg)), 6094: {error, joining} = qlc_SUITE:read_error_logger(), 6095: 20000 = length(F(info_msg)), 6096: {info, joining} = qlc_SUITE:read_error_logger(), 6097: 20000 = length(F(error_msg)), 6098: {error, joining} = qlc_SUITE:read_error_logger() 6099: after 6100: _ = erlang:system_flag(backtrace_depth, D) 6101: end, 6102: qlc_SUITE:uninstall_error_logger()">>], 6103: ?line run(Config, T1), 6104: 6105: T2 = [ 6106: <<"%% File sorter. 6107: T = lists:seq(1, 10000), 6108: Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 6109: [{cache,list},unique]), 6110: Q1 = qlc:q([{X,Y,Z} || 6111: X <- Q0, 6112: Y <- Q0, 6113: Z <- Q0], 6114: [{cache,list},unique]), 6115: Q = qlc:q([{X, Y} || Y <- [1], X <- Q1]), 6116: F = fun(Use) -> 6117: qlc:e(Q, [{max_list_size,10000},{tmpdir_usage,Use}]) 6118: end, 6119: 1 = length(F(allowed)), 6120: ErrReply = F(not_allowed), 6121: {error, qlc, {tmpdir_usage,caching}} = ErrReply, 6122: \"temporary file was needed for caching\n\" = 6123: lists:flatten(qlc:format_error(ErrReply)), 6124: qlc_SUITE:install_error_logger(), 6125: 1 = length(F(error_msg)), 6126: {error, caching} = qlc_SUITE:read_error_logger(), 6127: {error, caching} = qlc_SUITE:read_error_logger(), 6128: 1 = length(F(warning_msg)), 6129: {error, caching} = qlc_SUITE:read_error_logger(), 6130: {error, caching} = qlc_SUITE:read_error_logger(), 6131: 1 = length(F(info_msg)), 6132: {info, caching} = qlc_SUITE:read_error_logger(), 6133: {info, caching} = qlc_SUITE:read_error_logger(), 6134: qlc_SUITE:uninstall_error_logger()">>], 6135: 6136: ?line run(Config, T2), 6137: 6138: T3 = [ 6139: <<"%% sort/keysort 6140: E1 = create_ets(1, 10), 6141: E2 = create_ets(5, 50000), 6142: Q = qlc:q([{XX,YY} || 6143: {X,XX} <- ets:table(E1), 6144: {YY,Y} <- ets:table(E2), 6145: X == Y], 6146: {join,merge}), 6147: F = fun(Use) -> 6148: qlc:e(Q, {tmpdir_usage,Use}) 6149: end, 6150: ErrReply = F(not_allowed), 6151: {error,qlc,{tmpdir_usage,sorting}} = ErrReply, 6152: \"temporary file was needed for sorting\n\" = 6153: lists:flatten(qlc:format_error(ErrReply)), 6154: qlc_SUITE:install_error_logger(), 6155: L = [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}], 6156: L = F(allowed), 6157: L = F(error_msg), 6158: {error, sorting} = qlc_SUITE:read_error_logger(), 6159: L = F(info_msg), 6160: {info, sorting} = qlc_SUITE:read_error_logger(), 6161: L = F(warning_msg), 6162: {error, sorting} = qlc_SUITE:read_error_logger(), 6163: qlc_SUITE:uninstall_error_logger(), 6164: ets:delete(E1), 6165: ets:delete(E2)">>], 6166: ?line run(Config, T3), 6167: 6168: T4 = [ 6169: <<"%% cache list 6170: etsc(fun(E) -> 6171: Q0 = qlc:q([X || X <- ets:table(E), 6172: begin element(1, X) > 5 end], 6173: {cache,list}), 6174: Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5), 6175: Y <- Q0]), 6176: R = [{X,Y} || X <- lists:seq(1, 5), 6177: Y <- lists:seq(6, 10)], 6178: F = fun(Use) -> 6179: qlc:e(Q, [{max_list_size, 100*1024}, 6180: {tmpdir_usage, Use}]) 6181: end, 6182: R = lists:sort(F(allowed)), 6183: qlc_SUITE:install_error_logger(), 6184: R = lists:sort(F(info_msg)), 6185: {info, caching} = qlc_SUITE:read_error_logger(), 6186: R = lists:sort(F(error_msg)), 6187: {error, caching} = qlc_SUITE:read_error_logger(), 6188: R = lists:sort(F(warning_msg)), 6189: {error, caching} = qlc_SUITE:read_error_logger(), 6190: qlc_SUITE:uninstall_error_logger(), 6191: ErrReply = F(not_allowed), 6192: {error,qlc,{tmpdir_usage,caching}} = ErrReply, 6193: \"temporary file was needed for caching\n\" = 6194: lists:flatten(qlc:format_error(ErrReply)) 6195: end, [{keypos,1}], [{I,a,lists:duplicate(100000,1)} || 6196: I <- lists:seq(1, 10)])">>], 6197: ?line run(Config, T4), 6198: ok. 6199: 6200: otp_7238(doc) -> 6201: "OTP-7238. info-option 'depth', &c."; 6202: otp_7238(suite) -> []; 6203: otp_7238(Config) when is_list(Config) -> 6204: dets:start(), 6205: T = otp_7238, 6206: Fname = filename(T, Config), 6207: 6208: ?line ok = compile_gb_table(Config), 6209: 6210: %% A few more warnings. 6211: T1 = [ 6212: %% The same error message string, but with different tags 6213: %% (the strings are not compared :-( 6214: {nomatch_1, 6215: <<"nomatch_1() -> 6216: {qlc:q([X || X={X} <- []]), [t || \"a\"=\"b\" <- []]}.">>, 6217: [], 6218: {warnings,[{{2,30},qlc,nomatch_pattern}, 6219: {{2,44},v3_core,nomatch}]}}, 6220: 6221: %% Not found by qlc... 6222: {nomatch_2, 6223: <<"nomatch_2() -> 6224: qlc:q([t || {\"a\"++\"b\"} = {\"ac\"} <- []]).">>, 6225: [], 6226: {warnings,[{{2,22},v3_core,nomatch}]}}, 6227: 6228: {nomatch_3, 6229: <<"nomatch_3() -> 6230: qlc:q([t || [$a, $b] = \"ba\" <- []]).">>, 6231: [], 6232: {warnings,[{{2,37},qlc,nomatch_pattern}]}}, 6233: 6234: %% Not found by qlc... 6235: {nomatch_4, 6236: <<"nomatch_4() -> 6237: qlc:q([t || \"a\"++_=\"b\" <- []]).">>, 6238: [], 6239: {warnings,[{{2,22},v3_core,nomatch}]}}, 6240: 6241: %% Found neither by the compiler nor by qlc... 6242: {nomatch_5, 6243: <<"nomatch_5() -> 6244: qlc:q([X || X = <<X>> <- [3]]).">>, 6245: [], 6246: []}, 6247: 6248: {nomatch_6, 6249: <<"nomatch_6() -> 6250: qlc:q([X || X <- [], 6251: X =:= {X}]).">>, 6252: [], 6253: {warnings,[{{3,30},qlc,nomatch_filter}]}}, 6254: 6255: {nomatch_7, 6256: <<"nomatch_7() -> 6257: qlc:q([X || {X=Y,{Y}=X} <- []]).">>, 6258: [], 6259: {warnings,[{{2,28},qlc,nomatch_pattern}]}}, 6260: 6261: {nomatch_8, 6262: <<"nomatch_8() -> 6263: qlc:q([X || {X={},X=[]} <- []]).">>, 6264: [], 6265: {warnings,[{{2,28},qlc,nomatch_pattern}]}}, 6266: 6267: {nomatch_9, 6268: <<"nomatch_9() -> 6269: qlc:q([X || X <- [], X =:= {}, X =:= []]).">>, 6270: [], 6271: {warnings,[{{2,49},qlc,nomatch_filter}]}}, 6272: 6273: {nomatch_10, 6274: <<"nomatch_10() -> 6275: qlc:q([X || X <- [], 6276: ((X =:= 1) or (X =:= 2)) and (X =:= 3)]).">>, 6277: [], 6278: {warnings,[{{3,53},qlc,nomatch_filter}]}}, 6279: 6280: {nomatch_11, 6281: <<"nomatch_11() -> 6282: qlc:q([X || X <- [], x =:= []]).">>, 6283: [], 6284: {warnings,[{{2,39},qlc,nomatch_filter}]}}, 6285: 6286: {nomatch_12, 6287: <<"nomatch_12() -> 6288: qlc:q([X || X={} <- [], X =:= []]).">>, 6289: [], 6290: {warnings,[{{2,42},qlc,nomatch_filter}]}}, 6291: 6292: {nomatch_13, 6293: <<"nomatch_13() -> 6294: qlc:q([Z || Z <- [], 6295: X={X} <- [], 6296: Y={Y} <- []]).">>, 6297: [], 6298: {warnings,[{{3,29},qlc,nomatch_pattern}, 6299: {{4,29},qlc,nomatch_pattern}]}}, 6300: 6301: {nomatch_14, 6302: <<"nomatch_14() -> 6303: qlc:q([X || X={X} <- [], 6304: 1 > 0, 6305: 1 > X]).">>, 6306: [], 6307: {warnings,[{{2,29},qlc,nomatch_pattern}]}}, 6308: 6309: {nomatch_15, 6310: <<"nomatch_15() -> 6311: qlc:q([{X,Y} || X={X} <- [1], 6312: Y <- [1], 6313: 1 > 0, 6314: 1 > X]).">>, 6315: [], 6316: {warnings,[{{2,32},qlc,nomatch_pattern}]}}, 6317: 6318: %% Template warning. 6319: {nomatch_template1, 6320: <<"nomatch_template1() -> 6321: qlc:q([{X} = {} || X <- []]).">>, 6322: [], 6323: {warnings,[{2,sys_core_fold,no_clause_match}]}} 6324: ], 6325: ?line [] = compile(Config, T1), 6326: 6327: %% 'depth' is a new option used by info() 6328: T2 = [ 6329: %% Firstly: lists 6330: <<"L = [[a,b,c],{a,b,c},[],<<\"foobar\">>], 6331: Q = qlc:q([{X} || X <- L]), 6332: {call, _, 6333: {remote,_,{atom,_,ets},{atom,_,match_spec_run}}, 6334: [{cons,_,{atom,_,'...'}, 6335: {cons,_,{atom,_,'...'}, 6336: {cons,_,{nil,_},{cons,_,{atom,_,'...'},{nil,_}}}}}, 6337: _]} = qlc:info(Q, [{format,abstract_code},{depth,0}]), 6338: 6339: {call,_,_, 6340: [{cons,_,{cons,_,{atom,_,'...'},{nil,_}}, 6341: {cons,_, 6342: {tuple,_,[{atom,_,'...'}]}, 6343: {cons,_,{nil,_}, 6344: {cons,_, 6345: {bin,_, 6346: [{_,_,{_,_,$.},_,_}, 6347: {_,_,{_,_,$.},_,_}, 6348: {_,_,{_,_,$.},_,_}]}, 6349: {nil,_}}}}}, 6350: _]} = qlc:info(Q, [{format,abstract_code},{depth,1}]), 6351: 6352: {call,_, 6353: _, 6354: [{cons,_,{cons,_,{atom,_,a},{atom,_,'...'}}, 6355: {cons,_, 6356: {tuple,_,[{atom,_,a},{atom,_,'...'}]}, 6357: {cons,_,{nil,_}, 6358: {cons,_, 6359: {bin,_, 6360: [{_,_,{_,_,$f},_,_}, 6361: {_,_,{_,_,$.},_,_}, 6362: {_,_,{_,_,$.},_,_}, 6363: {_,_,{_,_,$.},_,_}]}, 6364: {nil,_}}}}}, 6365: _]} = qlc:info(Q, [{format,abstract_code},{depth,2}]), 6366: 6367: {call,_,_, 6368: [{cons,_, 6369: {cons,_,{atom,_,a},{cons,_,{atom,_,b},{atom,_,'...'}}}, 6370: {cons,_, 6371: {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,'...'}]}, 6372: {cons,_,{nil,_}, 6373: {cons,_, 6374: {bin,_, 6375: [{_,_,{_,_,$f},_,_}, 6376: {_,_,{_,_,$o},_,_},_,_,_]}, 6377: {nil,_}}}}}, 6378: _]} = qlc:info(Q, [{format,abstract_code},{depth,3}]), 6379: 6380: {call,_,_, 6381: [{cons,_, 6382: {cons,_, 6383: {atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c},{nil,_}}}}, 6384: {cons,_, 6385: {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,c}]}, 6386: {cons,_,{nil,_}, 6387: {cons,_, 6388: {bin,_, 6389: [{_,_,{_,_,$f},_,_}, 6390: {_,_,{_,_,$o},_,_}, 6391: {_,_,{_,_,$o},_,_}, 6392: {_,_,{_,_,$b},_,_}, 6393: {_,_,{_,_,$a},_,_}, 6394: {_,_,{_,_,$r},_,_}]}, 6395: {nil,_}}}}}, 6396: _]} = qlc:info(Q, [{format,abstract_code},{depth,10}]), 6397: 6398: {call,_,_, 6399: [{cons,_, 6400: {cons,_, 6401: {atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c},{nil,_}}}}, 6402: {cons,_, 6403: {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,c}]}, 6404: {cons,_,{nil,_}, 6405: {cons,_, 6406: {bin,_, 6407: [{_,_,{_,_,$f},_,_}, 6408: {_,_,{_,_,$o},_,_}, 6409: {_,_,{_,_,$o},_,_}, 6410: {_,_,{_,_,$b},_,_}, 6411: {_,_,{_,_,$a},_,_}, 6412: {_,_,{_,_,$r},_,_}]}, 6413: {nil,_}}}}}, 6414: _]} = qlc:info(Q, [{format,abstract_code},{depth,infinity}])">>, 6415: 6416: %% Secondly: looked up keys 6417: <<"F = fun(D) -> 6418: etsc(fun(E) -> 6419: Q = qlc:q([C || {N,C} <- ets:table(E), 6420: (N =:= {2,2}) or (N =:= {3,3})]), 6421: F = qlc:info(Q, [{format,abstract_code},{depth,D}]), 6422: {call,_,_,[{call,_,_,[_Fun,Values]},_]} = F, 6423: [b,c] = lists:sort(qlc:eval(Q)), 6424: Values 6425: end, [{{1,1},a},{{2,2},b},{{3,3},c},{{4,4},d}]) 6426: end, 6427: 6428: [{cons,_,{atom,_,'...'},{cons,_,{atom,_,'...'},{nil,_}}}, 6429: {cons,_, 6430: {tuple,_,[{atom,_,'...'}]}, 6431: {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}, 6432: {cons,_, 6433: {tuple,_,[{integer,_,2},{atom,_,'...'}]}, 6434: {cons,_,{tuple,_,[{integer,_,3},{atom,_,'...'}]},{nil,_}}}, 6435: {cons,_, 6436: {tuple,_,[{integer,_,2},{integer,_,2}]}, 6437: {cons,_,{tuple,_,[{integer,_,3},{integer,_,3}]},{nil,_}}}, 6438: {cons,_, 6439: {tuple,_,[{integer,_,2},{integer,_,2}]}, 6440: {cons,_,{tuple,_,[{integer,_,3},{integer,_,3}]},{nil,_}}}] = 6441: lists:map(F, [0,1,2,3,infinity])">>, 6442: [<<"T = otp_7238, Fname = \"">>, Fname, <<"\", 6443: {ok, _} = dets:open_file(T, [{file,Fname}]), 6444: ok = dets:insert(T, [{{1,1},a},{{2,2},b},{{3,3},c},{{4,4},d}]), 6445: Q = qlc:q([C || {N,C} <- dets:table(T), 6446: (N =:= {2,2}) or (N =:= {3,3})]), 6447: F = qlc:info(Q, [{format,abstract_code},{depth,1}]), 6448: [b,c] = lists:sort(qlc:eval(Q)), 6449: {call,_,_, 6450: [{call,_,_, 6451: [_, 6452: {cons,_, 6453: {tuple,_,[{atom,_,'...'}]}, 6454: {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}]}, 6455: _]} = F, 6456: ok = dets:close(T), 6457: file:delete(\"">>, Fname, <<"\")">>], 6458: 6459: %% Thirdly: format_fun has been extended (in particular: gb_table) 6460: <<"T = gb_trees:from_orddict([{{1,a},w},{{2,b},v},{{3,c},u}]), 6461: QH = qlc:q([X || {{X,Y},_} <- gb_table:table(T), 6462: ((X =:= 1) or (X =:= 2)), 6463: ((Y =:= a) or (Y =:= b) or (Y =:= c))]), 6464: {call,_,_, 6465: [{call,_,_, 6466: [{'fun',_, 6467: {clauses, 6468: [{clause,_,_,[], 6469: [{'case',_, 6470: {call,_,_, 6471: [_, 6472: {call,_,_, 6473: [{cons,_, 6474: {tuple,_,[{atom,_,'...'}]}, 6475: {cons,_, 6476: {tuple,_,[{atom,_,'...'}]}, 6477: {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}}]}]}, 6478: [_,_]}]}]}}, 6479: {cons,_, 6480: {tuple,_,[{atom,_,'...'}]}, 6481: {cons,_, 6482: {tuple,_,[{atom,_,'...'}]}, 6483: {cons,_, 6484: {tuple,_,[{atom,_,'...'}]}, 6485: {cons,_, 6486: {tuple,_,[{atom,_,'...'}]}, 6487: {cons,_, 6488: {tuple,_,[{atom,_,'...'}]}, 6489: {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}}}}}]}, 6490: {call,_,_, 6491: [{cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}]}]} = 6492: qlc:info(QH, [{format,abstract_code},{depth,1}])">>, 6493: <<"T1 = [{1,1,a},{2,2,b},{3,3,c},{4,4,d}], 6494: T2 = [{x,1},{y,1},{z,2}], 6495: QH1 = T1, 6496: T = gb_trees:from_orddict(T2), 6497: QH2 = qlc:q([X || {_,X} <- gb_table:table(T)], cache), 6498: Q = qlc:q([{X1,X2,X3} || {X1,X2,X3} <- QH1, 6499: Y2 <- QH2, 6500: X2 =:= Y2]), 6501: {block,_, 6502: [{match,_,_, 6503: {call,_,_, 6504: [{lc,_,_, 6505: [{generate,_,_, 6506: {call,_,_, 6507: [{call,_,_, 6508: [{cons,_, 6509: {tuple,_,[{atom,_,'...'}]}, 6510: {atom,_,'...'}}]}]}}]}, 6511: _]}}, 6512: {call,_,_, 6513: [{lc,_,_, 6514: [{generate,_,_, 6515: {cons,_,{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}}}, 6516: _,_]}]}]} = 6517: qlc:info(Q, [{format,abstract_code},{depth, 1}, 6518: {n_elements,1}])">>, 6519: <<"L = [{{key,1},a},{{key,2},b},{{key,3},c}], 6520: T = gb_trees:from_orddict(orddict:from_list(L)), 6521: Q = qlc:q([K || {K,_} <- gb_table:table(T), 6522: (K =:= {key,1}) or (K =:= {key,2})]), 6523: {call,_,_, 6524: [{call,_,_, 6525: [{'fun',_, 6526: {clauses, 6527: [{clause,_,_,[], 6528: [{'case',_, 6529: {call,_,_, 6530: [_, 6531: {call,_,_, 6532: [{cons,_, 6533: {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]}, 6534: {cons,_, 6535: {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]}, 6536: {cons,_, 6537: {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]}, 6538: {nil,_}}}}]}]}, 6539: _}]}]}}, 6540: {cons,_, 6541: {tuple,_,[{atom,_,key},{atom,_,'...'}]}, 6542: {cons,_,{tuple,_,[{atom,_,key},{atom,_,'...'}]},{nil,_}}}]}, 6543: {call,_, 6544: {remote,_,{atom,_,ets},{atom,_,match_spec_compile}}, 6545: [{cons,_, 6546: {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]}, 6547: {nil,_}}]}]} = 6548: qlc:info(Q, [{format,abstract_code},{depth, 2}])">> 6549: 6550: ], 6551: ?line run(Config, T2), 6552: 6553: T3 = [ 6554: {nomatch_6, 6555: <<"nomatch_6() -> 6556: qlc:q([X || X <- [], 6557: X =:= {X}]).">>, 6558: [], 6559: {[],["filter evaluates to 'false'"]}}, 6560: 6561: {nomatch_7, 6562: <<"nomatch_7() -> 6563: qlc:q([X || {X=Y,{Y}=X} <- []]).">>, 6564: [], 6565: {[],["pattern cannot possibly match"]}}], 6566: ?line compile_format(Config, T3), 6567: 6568: %% *Very* simple test - just check that it doesn't crash. 6569: Type = [{cres, 6570: <<"Q = qlc:q([X || {X} <- []]), 6571: {'EXIT',{{badfun,_},_}} = (catch qlc:e(Q))">>, 6572: [type_checker], 6573: []}], 6574: ?line run(Config, Type), 6575: 6576: ok. 6577: 6578: otp_7114(doc) -> 6579: "OTP-7114. Match spec, table and duplicated objects.."; 6580: otp_7114(suite) -> []; 6581: otp_7114(Config) when is_list(Config) -> 6582: Ts = [<<"T = ets:new(t, [bag]), 6583: [ets:insert(T, {t, I, I div 2}) || I <- lists:seq(1,10)], 6584: Q1 = qlc:q([element(3, E) || E <- ets:table(T)]), 6585: [0,1,1,2,2,3,3,4,4,5] = lists:sort(qlc:e(Q1)), 6586: [0,1,2,3,4,5] = qlc:e(Q1, unique_all), 6587: [0,1,2,3,4,5] = qlc:e(qlc:sort(Q1), unique_all), 6588: [0,1,2,3,4,5] = qlc:e(qlc:sort(qlc:e(Q1)), unique_all), 6589: ets:delete(T), 6590: ok">>], 6591: ?line run(Config, Ts). 6592: 6593: otp_7232(doc) -> 6594: "OTP-7232. qlc:info() bug (pids, ports, refs, funs)."; 6595: otp_7232(suite) -> []; 6596: otp_7232(Config) when is_list(Config) -> 6597: Ts = [<<"L = [fun math:sqrt/1, list_to_pid(\"<0.4.1>\"), 6598: erlang:make_ref()], 6599: \"[fun math:sqrt/1,<0.4.1>,#Ref<\" ++ _ = qlc:info(L), 6600: {call,_, 6601: {remote,_,{atom,_,qlc},{atom,_,sort}}, 6602: [{cons,_, 6603: {'fun',_,{function,{atom,_,math},{atom,_,sqrt},_}}, 6604: {cons,_, 6605: {string,_,\"<0.4.1>\"}, % could use list_to_pid.. 6606: {cons,_,{string,_,\"#Ref<\"++_},{nil,_}}}}, 6607: {nil,_}]} = 6608: qlc:info(qlc:sort(L),{format,abstract_code})">>, 6609: 6610: <<"Q1 = qlc:q([X || X <- [1000,2000]]), 6611: Q = qlc:sort(Q1, {order, fun(A,B)-> A>B end}), 6612: \"qlc:sort([1000,2000],[{order,fun'-function/0-fun-2-'/2}])\" = 6613: format_info(Q, true), 6614: AC = qlc:info(Q, {format, abstract_code}), 6615: \"qlc:sort([1000,2000], [{order,fun '-function/0-fun-2-'/2}])\" = 6616: binary_to_list(iolist_to_binary(erl_pp:expr(AC)))">>, 6617: 6618: %% OTP-7234. erl_parse:abstract() handles bit strings 6619: <<"Q = qlc:sort([<<17:9>>]), 6620: \"[<<8,1:1>>]\" = qlc:info(Q)">> 6621: 6622: ], 6623: ?line run(Config, Ts). 6624: 6625: otp_7552(doc) -> 6626: "OTP-7552. Merge join bug."; 6627: otp_7552(suite) -> []; 6628: otp_7552(Config) when is_list(Config) -> 6629: %% The poor performance cannot be observed unless the 6630: %% (redundant) join filter is skipped. 6631: Ts = [<<"Few = lists:seq(1, 2), 6632: Many = lists:seq(1, 10), 6633: S = [d,e], 6634: L1 = [{Y,a} || Y <- Few] ++ [{'1b',b},{2,b}] ++ 6635: [{Y,X} || X <- S, Y <- Few], 6636: L2 = [{a,Y} || Y <- Many] ++ 6637: [{b,'1b'}] ++ [{c,1}] ++ 6638: [{X,Y} || X <- S, Y <- Many], 6639: F = fun(J) -> 6640: qlc:q([{XX,YY} || 6641: {XX,X} <- L1, 6642: {Y,YY} <- L2, 6643: X == Y], 6644: {join,J}) 6645: end, 6646: Qm = F(merge), 6647: Qn = F(nested_loop), 6648: true = lists:sort(qlc:e(Qm, {max_list_size,20})) =:= 6649: lists:sort(qlc:e(Qn))">>], 6650: ?line run(Config, Ts). 6651: 6652: otp_7714(doc) -> 6653: "OTP-7714. Merge join bug."; 6654: otp_7714(suite) -> []; 6655: otp_7714(Config) when is_list(Config) -> 6656: %% The original example uses Mnesia. This one does not. 6657: Ts = [<<"E1 = ets:new(set,[]), 6658: true = ets:insert(E1, {a,1}), 6659: E2 = ets:new(set,[]), 6660: _ = [true = ets:insert(E2, {I, 1}) || 6661: I <- lists:seq(1, 3)], 6662: Q = qlc:q([{A,B} || 6663: {A,I1} <- ets:table(E1), 6664: {B,I2} <- ets:table(E2), 6665: I1 =:= I2],{join,merge}), 6666: [{a,1},{a,2},{a,3}] = lists:sort(qlc:e(Q)), 6667: ets:delete(E1), 6668: ets:delete(E2)">>], 6669: ?line run(Config, Ts). 6670: 6671: otp_6674(doc) -> 6672: "OTP-6674. match/comparison."; 6673: otp_6674(suite) -> []; 6674: otp_6674(Config) when is_list(Config) -> 6675: 6676: ?line ok = compile_gb_table(Config), 6677: 6678: Ts = [%% lookup join 6679: <<"E = ets:new(join, [ordered_set]), 6680: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 6681: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E), 6682: {Y} <- [{0},{1},{2}], 6683: X == Y]), 6684: {0,1,0,0} = join_info(Q), 6685: [{1,1},{2,2}] = qlc:e(Q), 6686: ets:delete(E)">>, 6687: 6688: <<"E = ets:new(join, [ordered_set]), 6689: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 6690: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E), 6691: {Y} <- [{0},{1},{2}], 6692: X =:= Y]), 6693: {0,1,0,0} = join_info(Q), 6694: {block,_, 6695: [_, 6696: {match,_,_, 6697: {call,_,_, 6698: [{lc,_,_, 6699: [_,_,{op,_,'==',_,_}]}, 6700: {cons,_, 6701: {tuple,_,[{atom,_,join},{atom,_,lookup}]},_}]}}, 6702: _]} = qlc:info(Q, {format, abstract_code}), 6703: [{1,1},{2,2}] = qlc:e(Q), 6704: ets:delete(E)">>, 6705: 6706: <<"E = ets:new(join, [set]), 6707: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E), 6708: {Y} <- [{0},{1},{2}], 6709: X == Y], {join, lookup}), 6710: {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(Q)), 6711: ets:delete(E)">>, 6712: 6713: %% Lookup join possible in both directions. 6714: <<"E1 = ets:new(join, [ordered_set]), 6715: E2 = ets:new(join, [set]), 6716: true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]), 6717: true = ets:insert(E2, [{0},{1},{2}]), 6718: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1), 6719: {Y} <- ets:table(E2), 6720: X == Y],{join,lookup}), % skipped 6721: {qlc,_, 6722: [{generate,_, 6723: {qlc,_, 6724: [{generate,_, 6725: {qlc,_,[{generate,_,{table,{ets,table,[_]}}}],[]}}, 6726: {generate,_,{table,{ets,table,[_]}}}, 6727: _], 6728: [{join,lookup}]}}], 6729: []} = qlc:info(Q, {format,debug}), 6730: {0,1,0,0} = join_info(Q), 6731: [{1.0,1},{2,2}] = lists:sort(qlc:e(Q)), 6732: ets:delete(E1), 6733: ets:delete(E2)">>, 6734: <<"E1 = ets:new(join, [ordered_set]), 6735: E2 = ets:new(join, [set]), 6736: true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]), 6737: true = ets:insert(E2, [{0},{1},{2}]), 6738: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1), 6739: {Y} <- ets:table(E2), 6740: X =:= Y],{join,merge}), % not skipped 6741: {1,0,0,1} = join_info(Q), 6742: [{2,2}] = qlc:e(Q), 6743: ets:delete(E1), 6744: ets:delete(E2)">>, 6745: <<"E1 = ets:new(join, [ordered_set]), 6746: E2 = ets:new(join, [set]), 6747: true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]), 6748: true = ets:insert(E2, [{0},{1},{2}]), 6749: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1), 6750: {Y} <- ets:table(E2), 6751: X =:= Y],{join,lookup}), % skipped 6752: {qlc,_, 6753: [{generate,_, 6754: {qlc,_, 6755: [{generate,_, 6756: {qlc,_, 6757: [{generate,_,{table,{ets,table,[_]}}}], 6758: []}}, 6759: {generate,_,{table,{ets,table,[_]}}}, 6760: _], 6761: [{join,lookup}]}}], 6762: []} = qlc:info(Q, {format,debug}), 6763: {0,1,0,0} = join_info(Q), 6764: [{2,2}] = qlc:e(Q), 6765: ets:delete(E1), 6766: ets:delete(E2)">>, 6767: <<"E1 = ets:new(join, [ordered_set]), 6768: E2 = ets:new(join, [set]), 6769: true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]), 6770: true = ets:insert(E2, [{0},{1},{2}]), 6771: Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1), 6772: {Y} <- ets:table(E2), 6773: %% Independent of term comparison: 6774: X =:= Y, X == Y]), 6775: {0,1,0,0} = join_info(Q), 6776: [{2,2}] = qlc:e(Q), 6777: ets:delete(E1), 6778: ets:delete(E2)">>, 6779: 6780: <<"E = ets:new(join, [ordered_set]), 6781: true = ets:insert(E, [{1,1},{2,2},{3,c}]), 6782: Q = qlc:q([{X, Y} || {X,Z} <- ets:table(E), 6783: {Y} <- [{0},{1},{2}], 6784: X == Z, Y == Z]), % cannot skip (yet) 6785: {qlc,_, 6786: [{generate,_, 6787: {qlc,_,[_,_,_],[{join,lookup}]}}, 6788: _,_],[]} = qlc:info(Q,{format,debug}), 6789: {0,1,0,0} = join_info(Q), 6790: [{1,1},{2,2}] = qlc:e(Q), 6791: ets:delete(E)">>, 6792: 6793: %% The following moved here from skip_filters. It was buggy! 6794: <<"etsc(fun(E) -> 6795: A = 3, 6796: Q = qlc:q([X || X <- ets:table(E), 6797: A == element(1,X)]), 6798: {table, _} = i(Q), 6799: case qlc:e(Q) of 6800: [{3},{3.0}] -> ok; 6801: [{3.0},{3}] -> ok 6802: end, 6803: false = lookup_keys(Q) 6804: end, [{3},{3.0},{c}])">>, 6805: <<"H1 = qlc:sort([{{192,192.0},1,a},{{192.0,192.0},2,b},{{192,192.0},3,c}]), 6806: Q = qlc:q([{X, Y} || {{A,B},X,_} <- H1, % does not need keysort(3) 6807: A == 192, B =:= 192.0, 6808: {Y} <- [{0},{1},{2}], 6809: X == Y]), 6810: {block,0, 6811: [{match,_,_, 6812: {call,_,_, 6813: [{lc,_,_, 6814: [_, 6815: %% Has to compare extra constant: 6816: {op,_,'==', 6817: {tuple,_,[{integer,_,192},{float,_,192.0}]}, 6818: {call,_,{atom,_,element},[{integer,_,1},{var,_,'P0'}]}}]}]}}, 6819: _,_, 6820: {call,_,_, 6821: [{lc,_,_, 6822: [_, 6823: %% The join filter has been skipped. 6824: {op,_,'==',{var,_,'A'},{integer,_,192}}, 6825: {op,_,'=:=',{var,_,'B'},{float,_,192.0}}]}]}]} 6826: = qlc:info(Q, {format,abstract_code}), 6827: {1,0,0,1} = join_info(Q), 6828: [{1,1},{2,2}] = qlc:e(Q)">>, 6829: 6830: %% Does not handle more than one lookup value (conjunctive). 6831: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6832: H = qlc:q([X || {X,_} <- gb_table:table(T), 6833: X =:= 1 andalso X == 1.0]), 6834: false = lookup_keys(H), 6835: [1] = qlc:e(H)">>, 6836: 6837: %% EqualConstants... 6838: <<"etsc(fun(E) -> 6839: Q = qlc:q([{X,Y} || {X} <- ets:table(E), 6840: {Y} <- [{{1}},{{2}},{{1.0}},{{2.0}}], 6841: X =:= {1}, X == {1.0}, 6842: X == Y], {join, merge}), 6843: [{{1},{1}},{{1},{1.0}}] = lists:sort(qlc:e(Q)), 6844: false = lookup_keys(Q) 6845: end, [{{1}}, {{2}}])">>, 6846: 6847: <<"T = gb_trees:from_orddict([{foo,{1}}, {bar,{2}}]), 6848: Q = qlc:q([{X,Y} || {_,X} <- gb_table:table(T), 6849: {Y} <- [{{1}},{{2}},{{1.0}},{{2.0}}], 6850: (X =:= {1}) or (X == {2}), 6851: (X == {1.0}) or (X =:= {2.0}), 6852: X == Y], {join, merge}), 6853: [{{1},{1}},{{1},{1.0}}] = qlc:e(Q)">>, 6854: 6855: %% Compare key 6856: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6857: H = qlc:q([X || {X,_} <- gb_table:table(T), 6858: X == 1]), 6859: [1] = lookup_keys(H), 6860: [1] = qlc:e(H)">>, 6861: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6862: H = qlc:q([X || {X,_} <- gb_table:table(T), 6863: X == 1.0]), 6864: [1.0] = lookup_keys(H), % this is how gb_table works... 6865: [1.0] = qlc:e(H)">>, 6866: <<"etsc(fun(E) -> 6867: H = qlc:q([X || {X,_} <- ets:table(E), 6868: X == 1.0]), 6869: [1] = qlc:e(H), % and this is how ETS works. 6870: [1.0] = lookup_keys(H) 6871: end, [ordered_set], [{1,a},{2,b}])">>, 6872: 6873: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6874: H = qlc:q([X || {X,_} <- gb_table:table(T), 6875: X =:= 2]), 6876: [2] = lookup_keys(H), 6877: %% Cannot (generally) remove the matching filter (the table 6878: %% compares the key). But note that gb_table returns the given 6879: %% term as key, so in this case the filter _could_ have been removed. 6880: %% However, there is no callback to inform qlc about that. 6881: {call,_,_, 6882: [_,{call,_,_, 6883: [{cons,_,{tuple,_, 6884: [_,{cons,_, 6885: {tuple,_,[{atom,_,'=:='},{atom,_,'$1'},{integer,_,2}]}, 6886: _},_]},_}]}]} = qlc:info(H, {format,abstract_code}), 6887: [2] = qlc:e(H)">>, 6888: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6889: H = qlc:q([X || {X,_} <- gb_table:table(T), 6890: X =:= 2.0]), 6891: %% Just shows that the term (not the key) is returned. 6892: [2.0] = lookup_keys(H), 6893: [2.0] = qlc:e(H)">>, 6894: 6895: <<"I = 1, 6896: T = gb_trees:from_orddict([{1,a},{2,b}]), 6897: H = qlc:q([X || {X,_} <- gb_table:table(T), 6898: X == I]), % imported variable 6899: [1] = lookup_keys(H), 6900: {call,_,_, 6901: [_,{call,_,_, 6902: [{cons,_, 6903: {tuple,_, 6904: [{tuple,_,[{atom,_,'$1'},{atom,_,'_'}]}, 6905: {nil,_}, % the filter has been skipped 6906: {cons,_,{atom,_,'$1'},_}]}, 6907: _}]}]} = qlc:info(H, {format, abstract_code}), 6908: [1] = qlc:e(H)">>, 6909: <<"I = 2, 6910: T = gb_trees:from_orddict([{1,a},{2,b}]), 6911: H = qlc:q([X || {X,_} <- gb_table:table(T), 6912: X =:= I]), 6913: [2] = lookup_keys(H), 6914: {call,_,_, 6915: [_,{call,_,_, 6916: [{cons,_,{tuple,_, 6917: [_,{cons,_, 6918: {tuple,_, 6919: [{atom,_,'=:='}, 6920: {atom,_,'$1'}, 6921: {tuple,_,[{atom,_,const},{integer,_,2}]}]}, 6922: _},_]}, 6923: _}]}]} = qlc:info(H, {format, abstract_code}), 6924: [2] = qlc:e(H)">>, 6925: 6926: <<"etsc(fun(E) -> 6927: Q = qlc:q([X || {X,_} <- ets:table(E), 6928: X =:= a]), % skipped 6929: [a] = qlc:e(Q), 6930: {list,{table,_},_} = i(Q), 6931: [a] = lookup_keys(Q) 6932: end, [ordered_set], [{a,1},{b,2},{3,c}])">>, 6933: 6934: %% Does not find that if for instance X =:= {1} then the filter 6935: %% X == {1} can be removed. 6936: <<"etsc(fun(E) -> 6937: Q = qlc:q([X || {X} <- ets:table(E), 6938: X =:= {1}, X == {1.0}]), 6939: [{1}] = qlc:e(Q), 6940: [{1}] = lookup_keys(Q) 6941: end, [{{1}}, {{2}}])">>, 6942: <<"etsc(fun(E) -> 6943: Q = qlc:q([X || {X} <- ets:table(E), 6944: X =:= {1}, X == {1.0}]), 6945: [{1}] = qlc:e(Q), 6946: false = lookup_keys(Q) 6947: end, [ordered_set], [{{1}}, {{2}}])">>, 6948: <<"etsc(fun(E) -> 6949: Q = qlc:q([X || {X} <- ets:table(E), 6950: X == {1.0}, X =:= {1}]), 6951: [{1}] = qlc:e(Q), 6952: [{1}] = lookup_keys(Q) 6953: end, [{{1}}, {{2}}])">>, 6954: <<"etsc(fun(E) -> 6955: Q = qlc:q([X || {X} <- ets:table(E), 6956: X == {1.0}, X =:= {1}]), 6957: [{1}] = qlc:e(Q), 6958: false = lookup_keys(Q) 6959: end, [ordered_set], [{{1}}, {{2}}])">>, 6960: 6961: <<"E = ets:new(apa, []), 6962: true = ets:insert(E, [{1,a},{2,b}]), 6963: {'EXIT', {badarg, _}} = 6964: (catch qlc_SUITE:bad_table_key_equality(E)), 6965: ets:delete(E)">>, 6966: 6967: <<"etsc(fun(E) -> 6968: Q = qlc:q([X || {X} <- ets:table(E), 6969: X =:= 1, X =:= is_integer(X)]), 6970: [] = qlc:e(Q), 6971: [1] = lookup_keys(Q) 6972: end, [{1}, {2}])">>, 6973: 6974: <<"etsc(fun(E) -> 6975: Q = qlc:q([X || {X=1} <- ets:table(E), 6976: X =:= is_integer(X)]), 6977: {call,_,_, 6978: [{lc,_,_, 6979: [_, 6980: {op,_,'=:=', 6981: {var,_,'X'}, 6982: {call,_, 6983: {atom,_,is_integer}, 6984: [{var,_,'X'}]}}]}]} = 6985: qlc:info(Q, {format, abstract_code}), 6986: [] = qlc:e(Q), 6987: [1] = lookup_keys(Q) 6988: end, [{1}, {2}])">>, 6989: 6990: <<"T = gb_trees:from_orddict([{1,a},{2,b}]), 6991: H = qlc:q([X || {X,Y} <- gb_table:table(T), 6992: Y =:= a, true, X =:= 1]), 6993: [1] = lookup_keys(H), 6994: [1] = qlc:e(H)">>, 6995: 6996: <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]), 6997: H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T), 6998: B == 1]), % skipped 6999: [{1.0, 1}] = qlc:e(H), 7000: {qlc,_,[{generate,_,{table,_}}], []} = qlc:info(H, {format,debug})">>, 7001: <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]), 7002: H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T), 7003: B == 1.0]), % skipped 7004: [{1.0, 1.0}] = qlc:e(H), % this is how gb_table works... 7005: {qlc,_,[{generate,_,{table,_}}], []} = qlc:info(H, {format,debug})">>, 7006: <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]), 7007: H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T), 7008: B =:= 1.0]), % not skipped 7009: [{1.0, 1.0}] = qlc:e(H), 7010: {qlc,_,[{generate,_,{table,_}},_], []} = qlc:info(H,{format,debug})">>, 7011: <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]), 7012: H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T), 7013: B =:= 1]), % not skipped 7014: [{1.0, 1}] = qlc:e(H), 7015: {qlc,_,[{generate,_,{table,_}},_], []} = qlc:info(H,{format,debug})">>, 7016: 7017: <<"%% The imported variables do not interfere with join. 7018: E = ets:new(join, [ordered_set]), 7019: {A, B} = {1,1}, 7020: true = ets:insert(E, [{1,a},{2,b},{3,c}]), 7021: Q = qlc:q([{X, Y} || {X,_Z} <- ets:table(E), 7022: {Y} <- [{0},{1},{2}], 7023: X =:= A, Y =:= B, 7024: Y == X], % skipped 7025: {join, merge}), 7026: [{1,1}] = qlc:e(Q), 7027: {qlc,_, 7028: [{generate,_, 7029: {qlc,_, 7030: [{generate,_, 7031: {qlc,_,[{generate,_,{list,{table,_},_}},_],[]}}, 7032: {generate,_, 7033: {qlc,_,[{generate,_,{list,_,_}},_],[]}}, 7034: _], 7035: [{join,merge}]}}], 7036: []} = qlc:info(Q, {format, debug}), 7037: ets:delete(E)">>, 7038: 7039: <<"% An old bug: usort() should not be used when matching values 7040: etsc(fun(E) -> 7041: I = 1, 7042: H = qlc:q([X || {X,_} <- ets:table(E), 7043: X =:= 1.0 orelse X =:= I]), 7044: [1] = qlc:e(H), 7045: [1.0] = lookup_keys(H) % do not look up twice 7046: end, [set], [{1,a},{2,b}])">>, 7047: <<"etsc(fun(E) -> 7048: H = qlc:q([X || {X,_} <- ets:table(E), 7049: X =:= 1.0 orelse X == 1]), 7050: [1] = qlc:e(H), 7051: false = lookup_keys(H) % doesn't handle this case 7052: end, [ordered_set], [{1,a},{2,b}])">>, 7053: 7054: <<"etsc(fun(E) -> 7055: I1 = 1, I2 = 1, 7056: H = qlc:q([X || {X,_} <- ets:table(E), 7057: X =:= I1 orelse X == I2]), 7058: [1] = qlc:e(H), % do not look up twice 7059: [1] = lookup_keys(H) 7060: end, [ordered_set], [{1,a},{2,b}])">>, 7061: 7062: <<"etsc(fun(E) -> 7063: I1 = 1, I2 = 1, I3 = 1, 7064: H = qlc:q([X || {X,_} <- ets:table(E), 7065: I1 == I2, I1 =:= I3, I3 == I2, I2 =:= I3, 7066: X =:= I1 orelse X == I2 7067: ]), 7068: [1] = qlc:e(H), 7069: [1] = lookup_keys(H) 7070: end, [ordered_set], [{1,a},{2,b}])">>, 7071: 7072: <<"E = ets:new(join, [ordered_set]), 7073: true = ets:insert(E, [{1,a},{2,b,x},{3,c}]), 7074: Q = qlc:q([P || P <- ets:table(E), 7075: P =:= {1,a} orelse P =:= {2,b,x}]), 7076: [{1,a},{2,b,x}] = qlc:e(Q), 7077: ets:delete(E)">>, 7078: 7079: <<"etsc(fun(E) -> 7080: Q = qlc:q([X || {X,Y} <- ets:table(E), 7081: ((X =:= 3) or (Y =:= 4)) and (X == a)]), 7082: {list,{table,_},_} = i(Q), 7083: [] = qlc:e(Q), % a is not an answer 7084: [a] = lookup_keys(Q) 7085: end, [{keypos,1},ordered_set], [{a,3},{b,4}])">>, 7086: 7087: <<"Q = qlc:q([{X,Y} || 7088: {X} <- [{<<3:4>>}], 7089: {Y} <- [{<<3:4>>}], 7090: X =:= <<1:3,1:1>>, % <<3:4>> 7091: Y =:= <<0:2,1:1,1:1>>, % <<3:4>> 7092: X =:= Y]), 7093: [{<<3:4>>,<<3:4>>}] = qlc:e(Q)">> 7094: 7095: 7096: ], 7097: 7098: ?line run(Config, Ts). 7099: 7100: manpage(doc) -> 7101: "Examples from qlc(3)."; 7102: manpage(suite) -> []; 7103: manpage(Config) when is_list(Config) -> 7104: 7105: ?line ok = compile_gb_table(Config), 7106: 7107: Ts = [ 7108: <<"QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]), 7109: QC = qlc:cursor(QH), 7110: [{a,1}] = qlc:next_answers(QC, 1), 7111: [{a,2}] = qlc:next_answers(QC, 1), 7112: [{b,1},{b,2}] = qlc:next_answers(QC, all_remaining), 7113: ok = qlc:delete_cursor(QC)">>, 7114: 7115: <<"QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]), 7116: [{a,1},{a,2},{b,1},{b,2}] = qlc:eval(QH)">>, 7117: 7118: <<"QH = [1,2,3,4,5,6], 7119: 21 = qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH)">>, 7120: 7121: <<"QH = qlc:q([{X,Y} || X <- [x,y], Y <- [a,b]]), 7122: B = \"begin\n\" 7123: \" V1 =\n\" 7124: \" qlc:q([ \n\" 7125: \" SQV ||\n\" 7126: \" SQV <- [x,y]\n\" 7127: \" ],\n\" 7128: \" [{unique,true}]),\n\" 7129: \" V2 =\n\" 7130: \" qlc:q([ \n\" 7131: \" SQV ||\n\" 7132: \" SQV <- [a,b]\n\" 7133: \" ],\n\" 7134: \" [{unique,true}]),\n\" 7135: \" qlc:q([ \n\" 7136: \" {X,Y} ||\n\" 7137: \" X <- V1,\n\" 7138: \" Y <- V2\n\" 7139: \" ],\n\" 7140: \" [{unique,true}])\n\" 7141: \"end\", 7142: true = B =:= qlc:info(QH, unique_all)">>, 7143: 7144: <<"E1 = ets:new(e1, []), 7145: E2 = ets:new(e2, []), 7146: true = ets:insert(E1, [{1,a},{2,b}]), 7147: true = ets:insert(E2, [{a,1},{b,2}]), 7148: Q = qlc:q([{X,Z,W} || 7149: {X, Z} <- ets:table(E1), 7150: {W, Y} <- ets:table(E2), 7151: X =:= Y]), 7152: L = \"begin\n\" 7153: \" V1 =\n\" 7154: \" qlc:q([ \n\" 7155: \" P0 ||\n\" 7156: \" P0 = {W,Y} <- ets:table(_)\n\" 7157: \" ]),\n\" 7158: \" V2 =\n\" 7159: \" qlc:q([ \n\" 7160: \" [G1|G2] ||\n\" 7161: \" G2 <- V1,\n\" 7162: \" G1 <- ets:table(_),\n\" 7163: \" element(2, G1) =:= element(1, G2)\n\" 7164: \" ],\n\" 7165: \" [{join,lookup}]),\n\" 7166: \" qlc:q([ \n\" 7167: \" {X,Z,W} ||\n\" 7168: \" [{X,Z}|{W,Y}] <- V2\n\" 7169: \" ])\n\" 7170: \"end\", 7171: Info = 7172: re:replace(qlc:info(Q), 7173: \"table\\\\(-*[0-9]*\", 7174: \"table(_\", [{return,list},global]), 7175: L = Info, 7176: ets:delete(E1), 7177: ets:delete(E2)">>, 7178: 7179: <<"Q = qlc:q([{A,X,Z,W} || 7180: A <- [a,b,c], 7181: {X,Z} <- [{a,1},{b,4},{c,6}], 7182: {W,Y} <- [{2,a},{3,b},{4,c}], 7183: X =:= Y], 7184: {cache, list}), 7185: L = 7186: \"begin\n\" 7187: \" V1 =\n\" 7188: \" qlc:q([ \n\" 7189: \" P0 ||\n\" 7190: \" P0 = {X,Z} <- qlc:keysort(1, [{a,1},{b,4},{c,6}], [])\n\" 7191: \" ]),\n\" 7192: \" V2 =\n\" 7193: \" qlc:q([ \n\" 7194: \" P0 ||\n\" 7195: \" P0 = {W,Y} <- qlc:keysort(2, [{2,a},{3,b},{4,c}], [])\n\" 7196: \" ]),\n\" 7197: \" V3 =\n\" 7198: \" qlc:q([ \n\" 7199: \" [G1|G2] ||\n\" 7200: \" G1 <- V1,\n\" 7201: \" G2 <- V2,\n\" 7202: \" element(1, G1) == element(2, G2)\n\" 7203: \" ],\n\" 7204: \" [{join,merge},{cache,list}]),\n\" 7205: \" qlc:q([ \n\" 7206: \" {A,X,Z,W} ||\n\" 7207: \" A <- [a,b,c],\n\" 7208: \" [{X,Z}|{W,Y}] <- V3,\n\" 7209: \" X =:= Y\n\" 7210: \" ])\n\" 7211: \"end\", 7212: L = qlc:info(Q)">>, 7213: 7214: <<"E1 = ets:new(t, [set]), % uses =:=/2 7215: Q1 = qlc:q([K || 7216: {K} <- ets:table(E1), 7217: K == 2.71 orelse K == a]), 7218: {list,{table,_}, [{{'$1'},[],['$1']}]} = i(Q1), 7219: true = ets:delete(E1)">>, 7220: 7221: <<"F = fun(E, I) -> 7222: qlc:q([V || {K,V} <- ets:table(E), K == I]) 7223: end, 7224: E2 = ets:new(t, [set]), 7225: true = ets:insert(E2, [{{2,2},a},{{2,2.0},b},{{2.0,2},c}]), 7226: Q2 = F(E2, {2,2}), 7227: {table,{ets,table,[_, 7228: [{traverse,{select,[{{'$1','$2'}, 7229: [{'==','$1',{const,{2,2}}}], 7230: ['$2']}]}}]]}} = i(Q2), 7231: [a,b,c] = lists:sort(qlc:e(Q2)), 7232: true = ets:delete(E2), 7233: 7234: E3 = ets:new(t, [ordered_set]), % uses ==/2 7235: true = ets:insert(E3, [{{2,2.0},b}]), 7236: Q3 = F(E3,{2,2}), 7237: {list,{table,_},[{{'$1','$2'},[],['$2']}]} = i(Q3), 7238: [b] = qlc:e(Q3), 7239: true = ets:delete(E3)">>, 7240: 7241: <<"T = gb_trees:empty(), 7242: QH = qlc:q([X || {{X,Y},_} <- gb_table:table(T), 7243: ((X == 1) or (X == 2)) andalso 7244: ((Y == a) or (Y == b) or (Y == c))]), 7245: L = \"ets:match_spec_run(lists:flatmap(fun(K) -> 7246: case 7247: gb_trees:lookup(K, 7248: gb_trees:from_orddict([])) 7249: of 7250: {value,V} -> 7251: [{K,V}]; 7252: none -> 7253: [] 7254: end 7255: end, 7256: [{1,a},{1,b},{1,c},{2,a},{2,b},{2,c}]), 7257: ets:match_spec_compile([{{{'$1','$2'},'_'},[],['$1']}]))\", 7258: L = qlc:info(QH)">> 7259: ], 7260: ?line run(Config, Ts), 7261: 7262: L = [1,2,3], 7263: Bs = erl_eval:add_binding('L', L, erl_eval:new_bindings()), 7264: QH = qlc:string_to_handle("[X+1 || X <- L].", [], Bs), 7265: [2,3,4] = qlc:eval(QH), 7266: 7267: %% ets(3) 7268: MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y} end), 7269: ETs = [ 7270: [<<"true = ets:insert(Tab = ets:new(t, []),[{1,a},{2,b},{3,c},{4,d}]), 7271: MS = ">>, io_lib:format("~w", [MS]), <<", 7272: QH1 = ets:table(Tab, [{traverse, {select, MS}}]), 7273: 7274: QH2 = qlc:q([{Y} || {X,Y} <- ets:table(Tab), (X > 1) or (X < 5)]), 7275: 7276: true = qlc:info(QH1) =:= qlc:info(QH2), 7277: true = ets:delete(Tab)">>]], 7278: ?line run(Config, ETs), 7279: 7280: %% dets(3) 7281: DTs = [ 7282: [<<"{ok, T} = dets:open_file(t, []), 7283: ok = dets:insert(T, [{1,a},{2,b},{3,c},{4,d}]), 7284: MS = ">>, io_lib:format("~w", [MS]), <<", 7285: QH1 = dets:table(T, [{traverse, {select, MS}}]), 7286: 7287: QH2 = qlc:q([{Y} || {X,Y} <- dets:table(t), (X > 1) or (X < 5)]), 7288: 7289: true = qlc:info(QH1) =:= qlc:info(QH2), 7290: ok = dets:close(T)">>]], 7291: ?line run(Config, DTs), 7292: 7293: ok. 7294: 7295: compile_gb_table(Config) -> 7296: GB_table_file = filename("gb_table.erl", Config), 7297: ?line ok = file:write_file(GB_table_file, gb_table()), 7298: ?line {ok, gb_table} = compile:file(GB_table_file, [{outdir,?privdir}]), 7299: ?line code:purge(gb_table), 7300: ?line {module, gb_table} = 7301: code:load_abs(filename:rootname(GB_table_file)), 7302: ok. 7303: 7304: gb_table() -> 7305: <<" 7306: -module(gb_table). 7307: 7308: -export([table/1]). 7309: 7310: table(T) -> 7311: TF = fun() -> qlc_next(gb_trees:next(gb_trees:iterator(T))) end, 7312: InfoFun = fun(num_of_objects) -> gb_trees:size(T); 7313: (keypos) -> 1; 7314: (is_sorted_key) -> true; 7315: (is_unique_objects) -> true; 7316: (_) -> undefined 7317: end, 7318: LookupFun = 7319: fun(1, Ks) -> 7320: lists:flatmap(fun(K) -> 7321: case gb_trees:lookup(K, T) of 7322: {value, V} -> [{K,V}]; 7323: none -> [] 7324: end 7325: end, Ks) 7326: end, 7327: FormatFun = 7328: fun({all, NElements, ElementFun}) -> 7329: ValsS = io_lib:format(\"gb_trees:from_orddict(~w)\", 7330: [gb_nodes(T, NElements, ElementFun)]), 7331: io_lib:format(\"gb_table:table(~s)\", [ValsS]); 7332: ({lookup, 1, KeyValues, _NElements, ElementFun}) -> 7333: ValsS = io_lib:format(\"gb_trees:from_orddict(~w)\", 7334: [gb_nodes(T, infinity, ElementFun)]), 7335: io_lib:format(\"lists:flatmap(fun(K) -> \" 7336: \"case gb_trees:lookup(K, ~s) of \" 7337: \"{value, V} -> [{K,V}];none -> [] end \" 7338: \"end, ~w)\", 7339: [ValsS, [ElementFun(KV) || KV <- KeyValues]]) 7340: end, 7341: qlc:table(TF, [{info_fun, InfoFun}, {format_fun, FormatFun}, 7342: {lookup_fun, LookupFun},{key_equality,'=='}]). 7343: 7344: qlc_next({X, V, S}) -> 7345: [{X,V} | fun() -> qlc_next(gb_trees:next(S)) end]; 7346: qlc_next(none) -> 7347: []. 7348: 7349: gb_nodes(T, infinity, ElementFun) -> 7350: gb_nodes(T, -1, ElementFun); 7351: gb_nodes(T, NElements, ElementFun) -> 7352: gb_iter(gb_trees:iterator(T), NElements, ElementFun). 7353: 7354: gb_iter(_I, 0, _EFun) -> 7355: '...'; 7356: gb_iter(I0, N, EFun) -> 7357: case gb_trees:next(I0) of 7358: {X, V, I} -> 7359: [EFun({X,V}) | gb_iter(I, N-1, EFun)]; 7360: none -> 7361: [] 7362: end. 7363: ">>. 7364: 7365: 7366: backward(doc) -> 7367: "OTP-6674. Join info and extra constants."; 7368: backward(suite) -> []; 7369: backward(Config) when is_list(Config) -> 7370: try_old_join_info(Config), 7371: ok. 7372: 7373: try_old_join_info(Config) -> 7374: %% Check join info for handlers of extra constants. 7375: File = filename:join(?datadir, "join_info_compat.erl"), 7376: M = join_info_compat, 7377: {ok, M} = compile:file(File, [{outdir, ?datadir}]), 7378: {module, M} = code:load_abs(filename:rootname(File)), 7379: H = M:create_handle(), 7380: {block,0, 7381: [{match,_,_, 7382: {call,_,_, 7383: [{lc,_,_, 7384: [_, 7385: {op,_,'=:=', 7386: {float,_,192.0}, 7387: {call,_,{atom,_,element},[{integer,_,1},_]}}]}]}}, 7388: _,_, 7389: {call,_,_, 7390: [{lc,_,_, 7391: [_, 7392: {op,_,'=:=',{var,_,'B'},{float,_,192.0}}, 7393: {op,_,'==',{var,_,'X'},{var,_,'Y'}}]}]}]} 7394: = qlc:info(H,{format,abstract_code}), 7395: [{1,1},{2,2}] = qlc:e(H), 7396: 7397: H2 = M:lookup_handle(), 7398: {qlc,_,[{generate,_,{qlc,_,_,[{join,lookup}]}},_],[]} = 7399: qlc:info(H2, {format,debug}), 7400: [{1,1},{2,2}] = qlc:e(H2). 7401: 7402: forward(doc) -> 7403: ""; 7404: forward(suite) -> []; 7405: forward(Config) when is_list(Config) -> 7406: Ts = [ 7407: %% LC_fun() returns something unknown. 7408: <<"FakeH = {qlc_handle,{qlc_lc,fun() -> {foo,bar} end, 7409: {qlc_opt,false,false,-1,any,[],any,524288}}}, 7410: {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">>, 7411: 7412: %% 'f1' should be used for new stuff that does not interfer with old behavior 7413: % %% The unused element 'f1' of #qlc_table seems to be used. 7414: % <<"DF = fun() -> foo end, 7415: % FakeH = {qlc_handle,{qlc_table,DF, 7416: % true,DF,DF,DF,DF,DF, 7417: % undefined,not_undefined,undefined,no_match_spec}}, 7418: % {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">>, 7419: 7420: %% #qlc_opt has changed. 7421: <<"H = qlc:q([X || X <- []]), 7422: {qlc_handle, {qlc_lc, Fun, _Opt}} = H, 7423: FakeH = {qlc_handle, {qlc_lc, Fun, {new_qlc_opt, a,b,c}}}, 7424: {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">> 7425: 7426: ], 7427: ?line run(Config, Ts), 7428: ok. 7429: 7430: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 7431: 7432: bad_table_throw(Tab) -> 7433: Limit = 1, 7434: Select = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7435: PreFun = fun(_) -> throw({throw,bad_pre_fun}) end, 7436: PostFun = fun() -> throw({throw,bad_post_fun}) end, 7437: InfoFun = fun(Tag) -> info(Tab, Tag) end, 7438: qlc:table(Select, [{pre_fun,PreFun}, {post_fun, PostFun}, 7439: {info_fun, InfoFun}]). 7440: 7441: bad_table_exit(Tab) -> 7442: Limit = 1, 7443: Select = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7444: PreFun = fun(_) -> erlang:error(bad_pre_fun) end, 7445: PostFun = fun() -> erlang:error(bad_post_fun) end, 7446: InfoFun = fun(Tag) -> info(Tab, Tag) end, 7447: qlc:table(Select, [{pre_fun,PreFun}, {post_fun, PostFun}, 7448: {info_fun, InfoFun}]). 7449: 7450: info(_Tab, is_unique_objects) -> 7451: false; 7452: info(Tab, Tag) -> 7453: try ets:info(Tab, Tag) catch _:_ -> undefined end. 7454: 7455: create_ets(S, E) -> 7456: create_ets(lists:seq(S, E)). 7457: 7458: create_ets(L) -> 7459: E1 = ets:new(e, []), 7460: true = ets:insert(E1, [{X,X} || X <- L]), 7461: E1. 7462: 7463: etsc(F, Objs) -> 7464: etsc(F, [{keypos,1}], Objs). 7465: 7466: etsc(F, Opts, Objs) -> 7467: E = ets:new(test, Opts), 7468: true = ets:insert(E, Objs), 7469: V = F(E), 7470: ets:delete(E), 7471: V. 7472: 7473: join_info(H) -> 7474: {qlc, S, Options} = strip_qlc_call(H), 7475: %% "Hide" the call to qlc_pt from the test in run_test(). 7476: LoadedPT = code:is_loaded(qlc_pt), 7477: QH = qlc:string_to_handle(S, Options), 7478: _ = [unload_pt() || false <- [LoadedPT]], % doesn't take long... 7479: case {join_info_count(H), join_info_count(QH)} of 7480: {N, N} -> 7481: N; 7482: Ns -> 7483: Ns 7484: end. 7485: 7486: strip_qlc_call(H) -> 7487: S = qlc:info(H, {flat, false}), 7488: {ok, Tokens, _EndLine} = erl_scan:string(S++"."), 7489: {ok, [Expr]} = erl_parse:parse_exprs(Tokens), 7490: case Expr of 7491: {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC]} -> 7492: {qlc, lists:flatten([erl_pp:expr(LC), "."]), []}; 7493: {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC, Opts]} -> 7494: {qlc, lists:flatten([erl_pp:expr(LC), "."]), 7495: erl_parse:normalise(Opts)}; 7496: {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} -> 7497: {match_spec, Expr}; 7498: {call,_,{remote,_,{atom,_,M},{atom,_,table}},_} -> 7499: {table, M, Expr}; 7500: _ -> 7501: [] 7502: end. 7503: 7504: -record(ji, {nmerge = 0, nlookup = 0, nnested_loop = 0, nkeysort = 0}). 7505: 7506: %% Counts join options and (all) calls to qlc:keysort(). 7507: join_info_count(H) -> 7508: S = qlc:info(H, {flat, false}), 7509: {ok, Tokens, _EndLine} = erl_scan:string(S++"."), 7510: {ok, [Expr]} = erl_parse:parse_exprs(Tokens), 7511: #ji{nmerge = Nmerge, nlookup = Nlookup, 7512: nkeysort = NKeysort, nnested_loop = Nnested_loop} = 7513: ji(Expr, #ji{}), 7514: {Nmerge, Nlookup, Nnested_loop, NKeysort}. 7515: 7516: ji({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC,Options]}, JI) -> 7517: NJI = case lists:keysearch(join, 1, erl_parse:normalise(Options)) of 7518: {value, {join, merge}} -> 7519: JI#ji{nmerge = JI#ji.nmerge + 1}; 7520: {value, {join, lookup}} -> 7521: JI#ji{nlookup = JI#ji.nlookup + 1}; 7522: {value, {join, nested_loop}} -> 7523: JI#ji{nnested_loop = JI#ji.nnested_loop + 1}; 7524: _ -> 7525: JI 7526: end, 7527: ji(LC, NJI); 7528: ji({call,_,{remote,_,{atom,_,qlc},{atom,_,keysort}},[_KP,H,_Options]}, JI) -> 7529: ji(H, JI#ji{nkeysort = JI#ji.nkeysort + 1}); 7530: ji(T, JI) when is_tuple(T) -> 7531: ji(tuple_to_list(T), JI); 7532: ji([E | Es], JI) -> 7533: ji(Es, ji(E, JI)); 7534: ji(_, JI) -> 7535: JI. 7536: 7537: %% Designed for ETS' and gb_table's format funs. 7538: lookup_keys(Q) -> 7539: case lists:flatten(lookup_keys(i(Q), [])) of 7540: [] -> false; 7541: L -> lists:usort(L) 7542: end. 7543: 7544: lookup_keys([Q | Qs], L) -> 7545: lookup_keys(Qs, lookup_keys(Q, L)); 7546: lookup_keys({qlc,_,Quals,_}, L) -> 7547: lookup_keys(Quals, L); 7548: lookup_keys({list,Q,_}, L) -> 7549: lookup_keys(Q, L); 7550: lookup_keys({generate,_,Q}, L) -> 7551: lookup_keys(Q, L); 7552: lookup_keys({table,Chars}, L) when is_list(Chars) -> 7553: {ok, Tokens, _} = erl_scan:string(lists:flatten(Chars++".")), 7554: {ok, [Expr]} = erl_parse:parse_exprs(Tokens), 7555: case Expr of 7556: {call,_,_,[_fun,AKs]} -> 7557: case erl_parse:normalise(AKs) of 7558: Ks when is_list(Ks) -> 7559: [lists:sort(Ks) | L]; 7560: K -> % assume keys are never lists (ets only) 7561: [K | L] 7562: end; 7563: _ -> % gb_table 7564: L 7565: end; 7566: lookup_keys(_Q, L) -> 7567: L. 7568: 7569: bad_table_format(Tab) -> 7570: Limit = 1, 7571: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7572: FormatFun = {is, no, good}, 7573: qlc:table(SelectFun, [{format_fun, FormatFun}]). 7574: 7575: bad_table_format_arity(Tab) -> 7576: Limit = 1, 7577: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7578: FormatFun = fun() -> {?MODULE, bad_table_format_arity, [Tab]} end, 7579: qlc:table(SelectFun, [{format_fun, FormatFun}]). 7580: 7581: bad_table_traverse(Tab) -> 7582: Limit = 1, 7583: Select = fun(MS, _) -> cb(ets:select(Tab, MS, Limit)) end, 7584: qlc:table(Select, []). 7585: 7586: bad_table_post(Tab) -> 7587: Limit = 1, 7588: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7589: qlc:table(SelectFun, [{pre_fun,undefined}, 7590: {post_fun, fun(X) -> X end}, 7591: {info_fun, undefined}]). 7592: 7593: bad_table_lookup(Tab) -> 7594: Limit = 1, 7595: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7596: qlc:table(SelectFun, {lookup_fun, fun(X) -> X end}). 7597: 7598: bad_table_max_lookup(Tab) -> 7599: Limit = 1, 7600: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7601: qlc:table(SelectFun, {max_lookup, -2}). 7602: 7603: bad_table_info_arity(Tab) -> 7604: Limit = 1, 7605: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7606: InfoFun = fun() -> {?MODULE, bad_table_info_arity, [Tab]} end, 7607: qlc:table(SelectFun, [{info_fun, InfoFun}]). 7608: 7609: default_table(Tab) -> 7610: Limit = 1, 7611: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7612: qlc:table(SelectFun, [{format_fun, undefined}, 7613: {info_fun, undefined}, 7614: {lookup_fun, undefined}, 7615: {parent_fun, undefined}, 7616: {pre_fun,undefined}, 7617: {post_fun, undefined}]). 7618: 7619: bad_table(Tab) -> 7620: Limit = 1, 7621: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7622: qlc:table(SelectFun, [{info, fun() -> ok end}]). 7623: 7624: bad_table_info_fun_n_objects(Tab) -> 7625: Limit = 1, 7626: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7627: LookupFun = fun(_Pos, Ks) -> 7628: lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 7629: end, 7630: InfoFun = fun(num_of_objects) -> exit(finito); 7631: (_) -> undefined 7632: end, 7633: qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]). 7634: 7635: bad_table_info_fun_indices(Tab) -> 7636: Limit = 1, 7637: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7638: LookupFun = fun(_Pos, Ks) -> 7639: lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 7640: end, 7641: InfoFun = fun(indices) -> throw({throw,apa}); 7642: (_) -> undefined 7643: end, 7644: qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]). 7645: 7646: bad_table_info_fun_keypos(Tab) -> 7647: Limit = 1, 7648: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7649: LookupFun = fun(_Pos, Ks) -> 7650: lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 7651: end, 7652: InfoFun = fun(indices) -> erlang:error(keypos); 7653: (_) -> undefined 7654: end, 7655: qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]). 7656: 7657: bad_table_key_equality(Tab) -> 7658: Limit = 1, 7659: SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end, 7660: LookupFun = fun(_Pos, Ks) -> 7661: lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 7662: end, 7663: qlc:table(SelectFun, [{lookup_fun, LookupFun},{key_equality,'=/='}]). 7664: 7665: cb('$end_of_table') -> 7666: []; 7667: cb({Objects,Cont}) -> 7668: Objects ++ fun() -> cb(ets:select(Cont)) end. 7669: 7670: i(H) -> 7671: i(H, []). 7672: 7673: i(H, Options) when is_list(Options) -> 7674: case has_format(Options) of 7675: true -> qlc:info(H, Options); 7676: false -> qlc:info(H, [{format, debug} | Options]) 7677: end; 7678: i(H, Option) -> 7679: i(H, [Option]). 7680: 7681: has_format({format,_}) -> 7682: true; 7683: has_format([E | Es]) -> 7684: has_format(E) or has_format(Es); 7685: has_format(_) -> 7686: false. 7687: 7688: format_info(H, Flat) -> 7689: L = qlc:info(H, [{flat, Flat}, {format,string}]), 7690: re:replace(L, "\s|\n|\t|\f|\r|\v", "", [{return,list},global]). 7691: 7692: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 7693: %% A list turned into a table... 7694: 7695: table_kill_parent(List, Indices) -> 7696: ParentFun = fun() -> exit(self(), kill) end, 7697: table_i(List, Indices, ParentFun). 7698: 7699: table_parent_throws(List, Indices) -> 7700: ParentFun = fun() -> throw({throw,thrown}) end, 7701: table_i(List, Indices, ParentFun). 7702: 7703: table_parent_exits(List, Indices) -> 7704: ParentFun = fun() -> 1 + Indices end, 7705: table_i(List, Indices, ParentFun). 7706: 7707: table_bad_parent_fun(List, Indices) -> 7708: ParentFun = fun(X) -> X end, % parent_fun should be nullary 7709: table_i(List, Indices, ParentFun). 7710: 7711: table(List, Indices) -> 7712: ParentFun = fun() -> self() end, 7713: table_i(List, Indices, ParentFun). 7714: 7715: table(List, KeyPos, Indices) -> 7716: ParentFun = fun() -> self() end, 7717: table(List, Indices, KeyPos, ParentFun). 7718: 7719: table_i(List, Indices, ParentFun) -> 7720: table(List, Indices, undefined, ParentFun). 7721: 7722: table(List, Indices, KeyPos, ParentFun) -> 7723: TraverseFun = fun() -> list_traverse(List) end, 7724: PreFun = fun(PreArgs) -> 7725: {value, {parent_value, Pid}} = 7726: lists:keysearch(parent_value, 1, PreArgs), 7727: true = is_pid(Pid) 7728: end, 7729: PostFun = fun() -> ok end, 7730: InfoFun = fun(indices) -> 7731: Indices; 7732: (is_unique_objects) -> 7733: undefined; 7734: (keypos) -> 7735: KeyPos; 7736: (num_of_objects) -> 7737: undefined; 7738: (_) -> 7739: undefined 7740: end, 7741: LookupFun = 7742: fun(Column, Values) -> 7743: lists:flatmap(fun(V) -> 7744: case lists:keysearch(V, Column, List) of 7745: false -> []; 7746: {value,Val} -> [Val] 7747: end 7748: end, Values) 7749: 7750: end, 7751: FormatFun = fun(all) -> 7752: L = 17, 7753: {call,L,{remote,L,{atom,1,?MODULE},{atom,L,the_list}}, 7754: [erl_parse:abstract(List, 17)]}; 7755: ({lookup, Column, Values}) -> 7756: {?MODULE, list_keys, [Values, Column, List]} 7757: end, 7758: qlc:table(TraverseFun, [{info_fun,InfoFun}, {pre_fun, PreFun}, 7759: {post_fun, PostFun}, {lookup_fun, LookupFun}, 7760: {format_fun, FormatFun}, 7761: {parent_fun, ParentFun}]). 7762: 7763: stop_list(List, Ets) -> 7764: Traverse = fun() -> list_traverse(List) end, 7765: PV = a_sample_parent_value, 7766: ParentFun = fun() -> PV end, 7767: Pre = fun(PreArgs) -> 7768: {value, {parent_value, PV}} = 7769: lists:keysearch(parent_value, 1, PreArgs), 7770: {value, {stop_fun, Fun}} = 7771: lists:keysearch(stop_fun, 1, PreArgs), 7772: true = ets:insert(Ets, {stop_fun, Fun}) 7773: end, 7774: qlc:table(Traverse, [{pre_fun, Pre}, {parent_fun, ParentFun}]). 7775: 7776: list_traverse([]) -> 7777: []; 7778: list_traverse([E | Es]) -> 7779: [E | fun() -> list_traverse(Es) end]. 7780: 7781: table_error(List, Error) -> 7782: table_error(List, undefined, Error). 7783: 7784: table_error(List, KeyPos, Error) -> 7785: TraverseFun = fun() -> list_traverse2(lists:sort(List), Error) end, 7786: InfoFun = fun(is_sorted_key) -> true; 7787: (keypos) -> KeyPos; 7788: (_) -> undefined 7789: end, 7790: qlc:table(TraverseFun, [{info_fun,InfoFun}]). 7791: 7792: list_traverse2([], Err) -> 7793: Err; 7794: list_traverse2([E | Es], Err) -> 7795: [E | fun() -> list_traverse2(Es, Err) end]. 7796: 7797: table_lookup_error(List) -> 7798: TraverseFun = fun() -> list_traverse(List) end, 7799: LookupFun = fun(_Column, _Values) -> {error,lookup,failed} end, 7800: InfoFun = fun(keypos) -> 1; 7801: (_) -> undefined 7802: end, 7803: qlc:table(TraverseFun, [{lookup_fun,LookupFun},{info_fun,InfoFun}]). 7804: 7805: prep_scratchdir(Dir) -> 7806: put('$qlc_tmpdir', true), 7807: _ = filelib:ensure_dir(Dir), 7808: lists:foreach(fun(F) -> file:delete(F) 7809: end, filelib:wildcard(filename:join(Dir, "*"))), 7810: true. 7811: 7812: %% Truncate just once. 7813: truncate_tmpfile(Dir, Where) -> 7814: case get('$qlc_tmpdir') of 7815: true -> 7816: {ok, [TmpFile0 | _]} = file:list_dir(Dir), 7817: TmpFile = filename:join(Dir, TmpFile0), 7818: truncate(TmpFile, Where), 7819: erase('$qlc_tmpdir'); 7820: _ -> 7821: true 7822: end. 7823: 7824: truncate(File, Where) -> 7825: {ok, Fd} = file:open(File, [read, write]), 7826: {ok, _} = file:position(Fd, Where), 7827: ok = file:truncate(Fd), 7828: ok = file:close(Fd). 7829: 7830: %% Crash just once. 7831: crash_tmpfile(Dir, Where) -> 7832: case get('$qlc_tmpdir') of 7833: true -> 7834: {ok, [TmpFile0 | _]} = file:list_dir(Dir), 7835: TmpFile = filename:join(Dir, TmpFile0), 7836: crash(TmpFile, Where), 7837: erase('$qlc_tmpdir'); 7838: _ -> 7839: true 7840: end. 7841: 7842: crash(File, Where) -> 7843: {ok, Fd} = file:open(File, [read, write]), 7844: {ok, _} = file:position(Fd, Where), 7845: ok = file:write(Fd, [10]), 7846: ok = file:close(Fd). 7847: 7848: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 7849: 7850: run(Config, Tests) -> 7851: run(Config, [], Tests). 7852: 7853: run(Config, Extra, Tests) -> 7854: lists:foreach(fun(Body) -> run_test(Config, Extra, Body) end, Tests). 7855: 7856: run_test(Config, Extra, {cres, Body, ExpectedCompileReturn}) -> 7857: run_test(Config, Extra, {cres, Body, _Opts = [], ExpectedCompileReturn}); 7858: run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) -> 7859: {SourceFile, Mod} = compile_file_mod(Config), 7860: P = [Extra,<<"function() -> ">>, Body, <<", ok. ">>], 7861: CompileReturn = compile_file(Config, P, Opts), 7862: case comp_compare(ExpectedCompileReturn, CompileReturn) of 7863: true -> ok; 7864: false -> expected(ExpectedCompileReturn, CompileReturn, SourceFile) 7865: end, 7866: AbsFile = filename:rootname(SourceFile, ".erl"), 7867: _ = code:purge(Mod), 7868: {module, _} = code:load_abs(AbsFile, Mod), 7869: 7870: Ms0 = erlang:process_info(self(),messages), 7871: Before = {get(), pps(), ets:all(), Ms0}, 7872: 7873: %% Prepare the check that the qlc module does not call qlc_pt. 7874: _ = [unload_pt() || {file, Name} <- [code:is_loaded(qlc_pt)], 7875: Name =/= cover_compiled], 7876: 7877: R = case catch Mod:function() of 7878: {'EXIT', _Reason} = Error -> 7879: ?t:format("failed, got ~p~n", [Error]), 7880: fail(SourceFile); 7881: Reply -> 7882: Reply 7883: end, 7884: 7885: %% Check that the qlc module does not call qlc_pt: 7886: case code:is_loaded(qlc_pt) of 7887: {file, cover_compiled} -> 7888: ok; 7889: {file, _} -> 7890: ?t:format("qlc_pt was loaded in runtime~n", []), 7891: fail(SourceFile); 7892: false -> 7893: ok 7894: end, 7895: 7896: wait_for_expected(R, Before, SourceFile, true), 7897: code:purge(Mod); 7898: run_test(Config, Extra, Body) -> 7899: run_test(Config, Extra, {cres,Body,[]}). 7900: 7901: wait_for_expected(R, Before, SourceFile, Wait) -> 7902: Ms = erlang:process_info(self(),messages), 7903: After = {get(), pps(), ets:all(), Ms}, 7904: case {R, After} of 7905: {ok, Before} -> 7906: ok; 7907: _ when Wait -> 7908: timer:sleep(1000), 7909: wait_for_expected(R, Before, SourceFile, false); 7910: _ -> 7911: expected({ok,Before}, {R,After}, SourceFile) 7912: end. 7913: 7914: unload_pt() -> 7915: erlang:garbage_collect(), % get rid of references to qlc_pt... 7916: _ = code:purge(qlc_pt), 7917: _ = code:delete(qlc_pt). 7918: 7919: compile_format(Config, Tests) -> 7920: Fun = fun(Test, Opts) -> 7921: Return = compile_file(Config, Test, Opts), 7922: format_messages(Return) 7923: end, 7924: compile(Config, Tests, Fun). 7925: 7926: format_messages({warnings,Ws}) -> 7927: format_messages({errors,[],Ws}); 7928: format_messages({errors,Es,Ws}) -> 7929: {[format_msg(E, Mod) || {_Line,Mod,E} <- Es], 7930: [format_msg(W, Mod) || {_Line,Mod,W} <- Ws]}. 7931: 7932: format_msg(Msg, Mod) -> 7933: IOlist = Mod:format_error(Msg), 7934: binary_to_list(iolist_to_binary(IOlist)). 7935: 7936: compile(Config, Tests) -> 7937: Fun = fun(Test, Opts) -> catch compile_file(Config, Test, Opts) end, 7938: compile(Config, Tests, Fun). 7939: 7940: compile(Config, Tests, Fun) -> 7941: F = fun({TestName,Test,Opts,Expected}, BadL) -> 7942: Return = Fun(Test, Opts), 7943: case comp_compare(Expected, Return) of 7944: true -> 7945: BadL; 7946: false -> 7947: {File, _Mod} = compile_file_mod(Config), 7948: expected(TestName, Expected, Return, File) 7949: end 7950: end, 7951: lists:foldl(F, [], Tests). 7952: 7953: %% Compiles a test module and returns the list of errors and warnings. 7954: 7955: compile_file(Config, Test0, Opts0) -> 7956: {File, Mod} = compile_file_mod(Config), 7957: Test = list_to_binary(["-module(", atom_to_list(Mod), "). " 7958: "-compile(export_all). " 7959: "-import(qlc_SUITE, [i/1,i/2,format_info/2]). " 7960: "-import(qlc_SUITE, [etsc/2, etsc/3]). " 7961: "-import(qlc_SUITE, [create_ets/2]). " 7962: "-import(qlc_SUITE, [strip_qlc_call/1]). " 7963: "-import(qlc_SUITE, [join_info/1]). " 7964: "-import(qlc_SUITE, [join_info_count/1]). " 7965: "-import(qlc_SUITE, [lookup_keys/1]). " 7966: "-include_lib(\"stdlib/include/qlc.hrl\"). ", 7967: Test0]), 7968: Opts = [export_all,return,nowarn_unused_record,{outdir,?privdir}|Opts0], 7969: ok = file:write_file(File, Test), 7970: case compile:file(File, Opts) of 7971: {ok, _M, Ws} -> warnings(File, Ws); 7972: {error, [{File,Es}], []} -> {errors, Es, []}; 7973: {error, [{File,Es}], [{File,Ws}]} -> {error, Es, Ws} 7974: end. 7975: 7976: comp_compare(T, T) -> 7977: true; 7978: comp_compare(T1, T2_0) -> 7979: T2 = wskip(T2_0), 7980: T1 =:= T2 7981: %% This clause should eventually be removed. 7982: orelse ln(T1) =:= T2 orelse T1 =:= ln(T2). 7983: 7984: wskip([]) -> 7985: []; 7986: wskip([{_,sys_core_fold,{eval_failure,badarg}}|L]) -> 7987: wskip(L); 7988: wskip([{{L,_C},sys_core_fold,M}|L]) -> 7989: [{L,sys_core_fold,M}|wskip(L)]; 7990: wskip({T,L}) -> 7991: {T,wskip(L)}; 7992: wskip([M|L]) -> 7993: [M|wskip(L)]; 7994: wskip(T) -> 7995: T. 7996: 7997: %% Replaces locations like {Line,Column} with Line. 7998: ln({warnings,L}) -> 7999: {warnings,ln0(L)}; 8000: ln({errors,EL,WL}) -> 8001: {errors,ln0(EL),ln0(WL)}; 8002: ln(L) -> 8003: ln0(L). 8004: 8005: ln0(L) -> 8006: lists:sort(ln1(L)). 8007: 8008: ln1([]) -> 8009: []; 8010: ln1([{File,Ms}|MsL]) when is_list(File) -> 8011: [{File,ln0(Ms)}|ln1(MsL)]; 8012: ln1([{{L,_C},Mod,Mess0}|Ms]) -> 8013: Mess = case Mess0 of 8014: {exported_var,V,{Where,{L1,_C1}}} -> 8015: {exported_var,V,{Where,L1}}; 8016: {unsafe_var,V,{Where,{L1,_C1}}} -> 8017: {unsafe_var,V,{Where,L1}}; 8018: %% There are more... 8019: M -> 8020: M 8021: end, 8022: [{L,Mod,Mess}|ln1(Ms)]; 8023: ln1([M|Ms]) -> 8024: [M|ln1(Ms)]. 8025: 8026: %% -> {FileName, Module}; {string(), atom()} 8027: compile_file_mod(Config) -> 8028: NameL = lists:concat([?TESTMODULE, "_", ?testcase]), 8029: Name = list_to_atom(NameL), 8030: File = filename(NameL ++ ".erl", Config), 8031: {File, Name}. 8032: 8033: filename(Name, Config) when is_atom(Name) -> 8034: filename(atom_to_list(Name), Config); 8035: filename(Name, Config) -> 8036: filename:join(?privdir, Name). 8037: 8038: pps() -> 8039: {port_list(), process_list()}. 8040: 8041: port_list() -> 8042: [{P,safe_second_element(erlang:port_info(P, name))} || 8043: P <- erlang:ports()]. 8044: 8045: process_list() -> 8046: [{P,process_info(P, registered_name), 8047: safe_second_element(process_info(P, initial_call))} || 8048: P <- processes(), is_process_alive(P)]. 8049: 8050: safe_second_element({_,Info}) -> Info; 8051: safe_second_element(Other) -> Other. 8052: 8053: warnings(File, Ws) -> 8054: case lists:append([W || {F, W} <- Ws, F =:= File]) of 8055: [] -> []; 8056: L -> {warnings, L} 8057: end. 8058: 8059: expected(Test, Expected, Got, File) -> 8060: ?t:format("~nTest ~p failed. ", [Test]), 8061: expected(Expected, Got, File). 8062: 8063: expected(Expected, Got, File) -> 8064: ?t:format("Expected~n ~p~n, but got~n ~p~n", [Expected, Got]), 8065: fail(File). 8066: 8067: fail(Source) -> 8068: io:format("failed~n"), 8069: ?t:fail({failed,testcase,on,Source}). 8070: 8071: %% Copied from global_SUITE.erl. 8072: 8073: install_error_logger() -> 8074: error_logger:add_report_handler(?MODULE, self()). 8075: 8076: uninstall_error_logger() -> 8077: error_logger:delete_report_handler(?MODULE). 8078: 8079: read_error_logger() -> 8080: receive 8081: {error, Why} -> 8082: {error, Why}; 8083: {info, Why} -> 8084: {info, Why}; 8085: {error, Pid, Tuple} -> 8086: {error, Pid, Tuple} 8087: after 1000 -> 8088: ?line io:format("No reply after 1 s\n", []), 8089: ?line ?t:fail() 8090: end. 8091: 8092: %%----------------------------------------------------------------- 8093: %% The error_logger handler used. 8094: %% (Copied from stdlib/test/proc_lib_SUITE.erl.) 8095: %%----------------------------------------------------------------- 8096: init(Tester) -> 8097: {ok, Tester}. 8098: 8099: handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) 8100: when is_atom(Why) -> 8101: Tester ! {error, Why}, 8102: {ok, Tester}; 8103: handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) -> 8104: Tester ! {error, P, T}, 8105: {ok, Tester}; 8106: handle_event({info_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) -> 8107: Tester ! {info, Why}, 8108: {ok, Tester}; 8109: handle_event(_Event, State) -> 8110: {ok, State}. 8111: 8112: handle_info(_, State) -> 8113: {ok, State}. 8114: 8115: handle_call(_Query, State) -> {ok, {error, bad_query}, State}. 8116: 8117: terminate(_Reason, State) -> 8118: State.