aboutsummaryrefslogblamecommitdiffstats
path: root/lib/stdlib/test/qlc_SUITE.erl
blob: 8a43f15d2cb0fe83646b4b6d4d64019b22592cfd (plain) (tree)
1
2
3
4
5
6
7
8
9

                   
  
                                                        
  


                                                                   
  






                                                                           
  









                                                                    
                       
















                                                                      
                                           


                                                          



                                                


                                                                    
 
          

                                                                    
                                                           
 




                                                                           
 
                        
 

                                                                    
 

                                                                         
                                                                      
                      
 
                    
 

                                
                    

























                                                                            
                                  
                                 
 
                                   

       


                                 
 
         


                                                           


                          

                                                        
                                                  




                                                              
                                      






                                                            
                                                                      
                                        
 





                         
                                     
           

                                    
           
 

                                      

                                      

                           







                                 
          





                                                  

       
                          






































                                                                          
                             

       
                                                             










                                                             
                             

       
                          







                                                                    
                             

           
                            














                                                                              
                             

       
                                 























                                                                          
                             


       
                           








                                                         
                             

       
                                                           















                                                                           
                             

       
                                                         




















                                                                           
                             

       
                                                 

















                                                           

                                                      









                                                        

                                                      










                                                                     

                                                      

























                                                                        
                             


       
                                                           







                                                            
                             

       
            




















                                                                           
                                     


                                                      
















                                                                         
 
         


















































































































                                                                              
                    

       
           











































































































                                                                              
                    

       
         
























































































                                                                              
                    

       
                                      










































































                                                                             

                                                                          


                                                                             
                                           










                                                                             
                    

       
                                                     































































































































                                                                            
                    

       
                            


























































                                                                                





                                                                              



















































                                                                            
                    

       
                              
                                         
                      
                            

                                                                    
                                


                            
                                                                   
                                               
                                  

                                                     

                                                                     


                                                        
                                  
                                                        
                                    

                                                        
                                  
                                                                            
                                    




                              
                                               
                                                                
 
                        
                                                

                                                           
                                                                    




                                                                    
                                     

                                                                    
                                                                          
                                      
                                            



                                                                        
                                                                            
                            
                                                                           
                              

                                                                       














                                                                         
                                                              
                                           
                                                                   
                    










                                                                        

       
        
















































































                                                                             




                                                                              


















































































































                                                                              
                    









                                                                        
                                                 


       
                                 









































































































































                                                                             
                    

       
                   

















































































                                                                             
                          

          
                    

       
                   





















































































































                                                                                   
                    


       
                             






                                                                          
                    


       
                    




























































































































































                                                                                
                    

       
                                     




























































































































































































































































































                                                                              
                    

       
                           
























































































































                                                                               
                    

       
          























                                                                     
                                                                                      
                                                    
                                                                  


                                




                                                                              

                                                                        

















































                                                                              
                                                                        






                                                              
                                                                                        








                                                                         
                                                                                       













































































































                                                                             









                                                                        

         
                    

       
                                                                  



































































































                                                                              
                    


       
                                        

















































































































                                                                              

                                                      


























                                                                     

                                                      






                                                                 

                                                      




























                                                                                
















                                                                     

                                                     

         
                    

       
                                       























                                                                  


                                                                     



























                                                                   

                                                     







                                                     

                                                     







                                                               

                                                     













                                                                          

                                                     






























                                                                             

                                                     








                                                                  

                                                     














                                                                         

                                                     



















































                                                                            
                        




                                                        
                               




















                                                                            

                                                     








                                                                    

                                                     


























































                                                                          

                                                     























































































































                                                                          

                         














                                                                        
             

















                                                                     
                                      

         

                                                    












































































































                                                                           










                                                                           
                                                                        












                                                                             
                                                                        



















































                                                                          
                          






























                                                                 

                          


       
                             















                                                                       

                                                     
















































                                                                        
                                              

       
                            




























































                                                                            
                                              

       
                                                               










































































                                                                         
                    

       
                             



































































































                                                                             

                                                     
















                                                                     
                     




















































































































































































































































































                                                                              
                    


       
 
                 



































                                                                              











                                                       


         
                    

       
                  



























































































                                                                               
                    


                           
 
                                                                          



























































































































                                                                                 
                    














































                                                                              
                      


       
                                       



































                                                                              
                    

       
               
























































































                                                                            
                    

       
              














































































































































































































































































                                                                              
                    

















































































































































































                                                                              
                     





























































































































































                                                                              
                     


       
                                                        






















































































































































































































































































                                                                               
                    

       
                                 


























                                                       
                        



























                                                                              
                                


       
 
                                                   






                                                                          
                    

       
                                                      












































































                                                                              
                    







                                                                             
                     


       
                                                  








                                                                            
                    



























                                                                    
                     


       
                                                        

















                                                       
                    

       
                                                                        

















                                                                        
                     










                                                        
                               
 
                                             
                                                                      
                                            



                                                 
                                 










                                                                              
                                                       


                                                               
                                     






















































                                                                              
                       



































                                                                             
                       
 

       
                                       















                                                                            
         






                                                          
                                                             


                                                          



                                                          
                                                 
                    
























                                                                          

                                                             




                                                          
                    
























                                                           
                                                             


                                             
                    





















                                                                            
                                                                          






                                                                      
                    

       
                                     




                                        
                                  








                                                                         


                                                   











                                                                 

                                                      



















                                                      

                                                     




                                                   

                                                      




                                                   

                                                      




                                                             

                                                     





                                                                        

                                                     




                                                   

                                                      




                                                      

                                                     






                                             


                                                      






                                       

                                                      







                                           

                                                      







                                                       
                             
































































































































































































































                                                                            
                    

          












                                                      
                               






                                                              
                      


       
                                                        









                                                                      
                    
 
                                                      


                                                            
                                                                      


                                                     
                                                                         





                                                                             
                                                   
                                                           
                                                                              

                                                       
                                                                                  






                                                                     
                    
 
                            





















                                                                        
                    
 
                            










                                                           
                                                        

                                
                    
 
                  









                                                                           
                              

                                        
                                  





















































                                                                             
                                                 













































































                                                                                

                            






























































































































































































































































































                                                                              
                    
 
                








                                                                   
                        
                                       
                 
                                  



















                                                              
                                                    
                                    
                                                    


                                         
                                                    
                                    
                                                    
                                
                                        


                                           
                                               














                                                   
                                                                  


                                     
                                               



                                                                        
                                                    
                                

                                                          

                                
                 
                                     
                                                      
                                                             



                                                        













                                                     

                                                                                



                                 


                                                                                


                              
                                        



                                                                
                                                           
                         


                                                    








































                                                                                      

                                                         



                                                   








                                                                

                             
                    
















                                                                              
                     











                                                                             
                     




                                                     



                                                                      































































                                                                            
 
                                           
                                        
                              
       
 
                            





                                                           

                         



















                                                              
 







                                                                             





                                                                               







                                                                           
                    

       







                                                              











































                                                              
                                                 

                                                             
                                              








                                                                    



                                     
                                   
                                                                
                                                             












                                                                      





                                                                       
                                                                
                                                              









































                                                                             
                                                                            
                                                              




































































































































































































                                                                              

                                                                              




















































































































                                                                             
                                                           






                                                                  
                                                       









                                                      
                                                            




                             




                                                   
                                                                
                                              
                                                                    
                      

                       
















                                                                    





                                                            
 











                                                                          











































                                                                        








                                                                          
                                                                                              



                                                     
                                                             































































                                                        










                                                                 











                                                             


                                               









                                                         
                                            


                                  
                                                                    


               
                                         


                                











                                                     

                           


                               
                                                  
                           








                                                                   
                                                                               







                                                                          
                                                                                     
                            
                 









                                                              
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%%----------------------------------------------------------------
%%% Purpose:Test Suite for the 'qlc' module.
%%%-----------------------------------------------------------------
-module(qlc_SUITE).

-define(QLC, qlc).
-define(QLCs, "qlc").

%%-define(debug, true).

%% There are often many tests per testcase. Most tests are copied to a
%% module, a file. The file is compiled and the test run. Should the
%% test fail, the module file is not removed from ?privdir, but is
%% kept for inspection. The name of the file is
%% ?privdir/qlc_test_CASE.erl.
-define(TESTMODULE, qlc_test).
-define(TESTCASE, testcase_name).

-ifdef(debug).
-define(line, put(line, ?LINE), ).
-define(config(X,Y), foo).
-define(datadir, ?QLCs ++  "_SUITE_data").
-define(privdir, ?QLCs ++ "_SUITE_priv").
-define(testcase, current_testcase). % don't know
-define(t, test_server).
-else.
-include_lib("common_test/include/ct.hrl").
-define(datadir, proplists:get_value(data_dir, Config)).
-define(privdir, proplists:get_value(priv_dir, Config)).
-define(testcase, proplists:get_value(?TESTCASE, Config)).
-endif.

-include_lib("stdlib/include/ms_transform.hrl").

-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2, 
	 init_per_testcase/2, end_per_testcase/2]).

-export([ 
	  badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1,
	  filter_var/1, single/1, exported_var/1, generator_vars/1,
	  nomatch/1, errors/1, pattern/1, overridden_bif/1,

	  eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1, 
	  evaluator/1, string_to_handle/1, table/1, process_dies/1, 
	  sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1, 
	  info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1, 
	  indices/1, pre_fun/1, skip_filters/1,

	  ets/1, dets/1,

	  join_option/1, join_filter/1, join_lookup/1, join_merge/1,
	  join_sort/1, join_complex/1,

	  otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
	  otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
	  otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, otp_11758/1,
          otp_12946/1,

	  manpage/1,

	  backward/1, forward/1,

	  eep37/1]).

%% Internal exports.
-export([bad_table_throw/1, bad_table_exit/1, default_table/1, bad_table/1,
         bad_table_format/1, bad_table_format_arity/1, bad_table_traverse/1,
         bad_table_post/1, bad_table_lookup/1, bad_table_max_lookup/1,
         bad_table_info_arity/1, bad_table_info_fun_n_objects/1,
         bad_table_info_fun_indices/1, bad_table_info_fun_keypos/1,
         bad_table_key_equality/1]).
-export([evaluator_2/2]).
-export([prep_scratchdir/1, truncate_tmpfile/2, crash/2, crash_tmpfile/2]).
-export([etsc/2, etsc/3, create_ets/2, lookup_keys/1]).
-export([strip_qlc_call/1, join_info/1, join_info_count/1]).
-export([i/1, i/2, format_info/2]).

-export([table_kill_parent/2, table_parent_throws/2, 
         table_parent_exits/2, table_bad_parent_fun/2]).
-export([table/2, table/3, stop_list/2, table_error/2, table_error/3, 
         table_lookup_error/1]).

%% error_logger
-export([install_error_logger/0, uninstall_error_logger/0, 
         read_error_logger/0]).
-export([init/1,
	 handle_event/2, handle_call/2, handle_info/2,
	 terminate/2]).

init_per_testcase(Case, Config) ->
    [{?TESTCASE, Case} | Config].

end_per_testcase(_Case, _Config) ->
    ok.

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap,{minutes,5}}].

all() -> 
    [{group, parse_transform}, {group, evaluation},
     {group, table_impls}, {group, join}, {group, tickets},
     manpage, {group, compat}].

groups() -> 
    [{parse_transform, [],
      [badarg, nested_qlc, unused_var, lc, fun_clauses,
       filter_var, single, exported_var, generator_vars,
       nomatch, errors, pattern, overridden_bif]},
     {evaluation, [],
      [eval, cursor, fold, eval_unique, eval_cache, append,
       evaluator, string_to_handle, table, process_dies, sort,
       keysort, filesort, cache, cache_list, filter, info,
       nested_info, lookup1, lookup2, lookup_rec, indices,
       pre_fun, skip_filters, eep37]},
     {table_impls, [], [ets, dets]},
     {join, [],
      [join_option, join_filter, join_lookup, join_merge,
       join_sort, join_complex]},
     {tickets, [],
      [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562,
       otp_6590, otp_6673, otp_6964, otp_7114, otp_7232,
       otp_7238, otp_7552, otp_6674, otp_7714, otp_11758, otp_12946]},
     {compat, [], [backward, forward]}].

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.

badarg(Config) when is_list(Config) ->
    Ts =
	[{badarg,
	  <<"-import(qlc, [q/1, q/2]).
          q(_, _, _) -> ok.

badarg() ->
    qlc:q(foo),
    qlc:q(foo, cache_all),
    qlc:q(foo, cache_all, extra),
    q(bar),
    q(bar, cache_all),
    q(bar, cache_all, extra).
">>,
       [],
{errors,[{5,?QLC,not_a_query_list_comprehension},
	 {6,?QLC,not_a_query_list_comprehension},
	 {8,?QLC,not_a_query_list_comprehension},
	 {9,?QLC,not_a_query_list_comprehension}],
 []}}],
    [] = compile(Config, Ts),
    ok.

%% Nested qlc expressions.
nested_qlc(Config) when is_list(Config) ->
    %% Nested QLC expressions. X is bound before the first one; Z and X
    %% before the second one.
    Ts = 
     [{nested_qlc1,
       <<"nested_qlc() ->
              X = 3, % X unused
              Q = qlc:q([Y || 
                            X <- % X shadowed
                                begin Z = 3, 
                                      qlc:q([Y || 
                                                Y <- [Z],
                                                X <- [1,2,3], % X shadowed
                                                X < Y])
                                end,
                            Y <- 
                                [y],
                            Y > X]),
              [y, y] = qlc:e(Q),
              ok.
       ">>,
       [warn_unused_vars],
       {warnings,[{{2,15},erl_lint,{unused_var,'X'}},
                  {{4,29},erl_lint,{shadowed_var,'X',generate}},
                  {{8,49},erl_lint,{shadowed_var,'X',generate}}]}},

      {nested_qlc2,
      <<"nested_qlc() ->
             H0 = qlc:append([a,b], [c,d]),
             qlc:q([{X,Y} || 
                       X <- H0,
                       Y <- qlc:q([{X,Y} || 
                                      X <- H0, % X shadowed
                                      Y <- H0])]),
             ok.
       ">>,
       [warn_unused_vars],
       {warnings,[{{6,39},erl_lint,{shadowed_var,'X',generate}}]}}
    ],
    [] = compile(Config, Ts),
    ok.

%% Unused variable with a name that should not be introduced.
unused_var(Config) when is_list(Config) ->
    Ts = 
     [{unused_var,
       <<"unused_var() ->
              qlc:q([X || begin Y1 = 3, true end, % Y1 unused
                          Y <- [1,2,3],
                          X <- [a,b,c],
                          X < Y]).
       ">>,
       [warn_unused_vars],
       {warnings,[{{2,33},erl_lint,{unused_var,'Y1'}}]}}],
    [] = compile(Config, Ts),
    ok.

%% Ordinary LC expression.
lc(Config) when is_list(Config) ->
    Ts = 
     [{lc,
       <<"lc() ->
              [X || X <- [], X <- X]. % X shadowed
        ">>,
       [],
       {warnings,[{{2,30},erl_lint,{shadowed_var,'X',generate}}]}}],
    [] = compile(Config, Ts),
    ok.
           
%% Fun with several clauses.
fun_clauses(Config) when is_list(Config) ->
    Ts = 
     [{fun_clauses,
       <<"fun_clauses() ->
            {X,X1,X2} = {1,2,3},
            F = fun({X}) -> qlc:q([X || X <- X]); % X shadowed (fun, generate)
                   ([X]) -> qlc:q([X || X <- X])  % X shadowed (fun, generate)
                end,
            {F,X,X1,X2}.
        ">>,
       [],
       {warnings,[{{3,22},erl_lint,{shadowed_var,'X','fun'}},
                  {{3,41},erl_lint,{shadowed_var,'X',generate}},
                  {{4,22},erl_lint,{shadowed_var,'X','fun'}},
                  {{4,41},erl_lint,{shadowed_var,'X',generate}}]}}],
    [] = compile(Config, Ts),
    ok.

%% Variable introduced in filter.
filter_var(Config) when is_list(Config) ->
    Ts = 
     [{filter_var,
       <<"filter_var() ->
              qlc:q([X || 
                  Y <- [X || 
                           X <- [1,2,3]],
                  begin X = Y, true end]).
        ">>,
       [],
       []},

      {unsafe_filter_var,
       <<"unsafe_filter_var() ->
              qlc:q([{X,V} || X <- [1,2],
                  case {a} of
                      {_} ->
                          true;
                      V ->
                          V
                  end]).
        ">>,
       [],
       {errors,[{{2,25},erl_lint,{unsafe_var,'V',{'case',{3,19}}}}],[]}}],
    [] = compile(Config, Ts),
    ok.


%% Unused pattern variable.
single(Config) when is_list(Config) ->
    Ts = 
     [{single,
       <<"single() ->
              qlc:q([X || {X,Y} <- [{1,2}]]), % Y unused
              qlc:q([[] || [] <- [[]]]).
        ">>,
       [warn_unused_vars],
       {warnings,[{{2,30},erl_lint,{unused_var,'Y'}}]}}],
    [] = compile(Config, Ts),
    ok.

%% Exported variable in list expression (rhs of generator).
exported_var(Config) when is_list(Config) ->
    Ts = 
     [{exported_var,
       <<"exported() ->
              qlc:q([X || X <- begin
                                   case foo:bar() of
                                       1 -> Z = a;
                                       2 -> Z = b
                                   end,
                                   [Z = 3, Z = 3] % Z exported (twice...)
                               end
                         ]).
       ">>,
       [warn_export_vars],
       {warnings,[{{7,37},erl_lint,{exported_var,'Z',{'case',{3,36}}}},
                  {{7,44},erl_lint,{exported_var,'Z',{'case',{3,36}}}}]}}],
    [] = compile(Config, Ts),
    ok.

%% Errors for generator variable used in list expression.
generator_vars(Config) when is_list(Config) ->
    Ts = 
      [{generator_vars,
        <<"generator_vars() ->
               qlc:q([X ||
                      Z <- [1,2],
                      X <- begin
                               case 1 of
                                   1 -> Z = a; % used_generator_variable
                                   2 -> Z = b % used_generator_variable
                               end,
                               [Z = 3, Z = 3] % used_generator_variable (2)
                           end
                     ]).
        ">>,
        [],
        {errors,[{{6,41},?QLC,{used_generator_variable,'Z'}},
                 {{7,41},?QLC,{used_generator_variable,'Z'}},
                 {{9,33},?QLC,{used_generator_variable,'Z'}},
                 {{9,40},?QLC,{used_generator_variable,'Z'}}],
         []}}],
    [] = compile(Config, Ts),
    ok.

%% Unreachable clauses also found when compiling.
nomatch(Config) when is_list(Config) ->
    Ts =
      [{unreachable1,
        <<"unreachable1() ->
               qlc:q([X || X <- [1,2],
                   case X of
                       true -> false;
                       true -> true   % clause cannot match
                    end]).
        ">>,
        [],
        {warnings,[{5,v3_kernel,{nomatch_shadow,4}}]}},

       {nomatch1,
        <<"generator1() ->
              qlc:q([3 || {3=4} <- []]).
        ">>,
        [],
        %% {warnings,[{{2,27},qlc,nomatch_pattern}]}},
        {warnings,[{2,v3_core,nomatch}]}},

       {nomatch2,
        <<"nomatch() ->
                etsc(fun(E) ->
                Q = qlc:q([3 || {3=4} <- ets:table(E)]),
                [] = qlc:eval(Q),
                false = lookup_keys(Q) 
          end, [{1},{2}]).
        ">>,
        [],
        %% {warnings,[{{3,33},qlc,nomatch_pattern}]}},
        {warnings,[{3,v3_core,nomatch}]}},
 
       {nomatch3,
        <<"nomatch() ->
              etsc(fun(E) ->
                          Q = qlc:q([{A,B,C,D} || A=B={C=D}={_,_} <- 
                                                    ets:table(E)]),
                          [] = qlc:eval(Q),
                          false = lookup_keys(Q)
                    end, [{1,2},{2,3}]).
        ">>,
        [],
        %% {warnings,[{{3,52},qlc,nomatch_pattern}]}},
        {warnings,[{3,v3_core,nomatch}]}},

       {nomatch4,
        <<"nomatch() ->
              etsc(fun(E) ->
                          Q = qlc:q([{X,Y} || {<<X>>} = {<<Y>>} <- 
                                                    ets:table(E)]),
                          [] = qlc:eval(Q),
                          false = lookup_keys(Q)
                    end, [{<<34>>},{<<40>>}]).
        ">>,
        [],
        {errors,[{{3,48},erl_lint,illegal_bin_pattern}],[]}},

       {nomatch5,
        <<"nomatch() ->
               etsc(fun(E) ->
                           Q = qlc:q([t || {\"a\"++\"b\"} = {\"ac\"} <- 
                                                    ets:table(E)]),
                           [t] = qlc:eval(Q),
                           [\"ab\"] = lookup_keys(Q)
                     end, [{\"ab\"}]).
        ">>,
        [],
        {warnings,[{3,v3_core,nomatch}]}}

      ],
    [] = compile(Config, Ts),
    ok.
    

%% Errors within qlc expressions also found when compiling.
errors(Config) when is_list(Config) ->
    Ts =
      [{errors1,
        <<"errors1() ->
               qlc:q([X || X <- A]). % A unbound
        ">>,
        [],
        {errors,[{{2,33},erl_lint,{unbound_var,'A'}}],[]}}],
    [] = compile(Config, Ts),
    ok.

%% Patterns.
pattern(Config) when is_list(Config) ->
    Ts = [
      <<"%% Records in patterns. No lookup.
         L = [#a{k=#k{v=91}}],
         H = qlc:q([Q || Q = #a{k=#k{v=91}} <- qlc_SUITE:table(L, 2, [])]),
         {qlc,_,[{generate,_,{table,{call,_,_,_}}}], []} = i(H),
         L = qlc:e(H),
         {call, _, _q, [{lc,_,{var,_,'Q'}, 
                         [{generate,_,
                           {match,_,_,_},
                           {call,_,_,_}}]}]}
              = i(H, {format,abstract_code})">>,

      <<"%% No matchspec since there is a binary in the pattern.
         etsc(fun(E) ->
             Q = qlc:q([A || {<<A:3/unit:8>>} <- ets:table(E)]),
             [_] = qlc:eval(Q),
             {qlc,_,[{generate,_,{table,_}}], []} = i(Q)
         end, [{<<\"hej\">>}])">>

       ],
    run(Config, <<"-record(a, {k,v}).
                         -record(k, {t,v}).\n">>, Ts),
    ok.

%% Override a guard BIF with an imported or local function.
overridden_bif(Config) ->
    Ts = [
	  <<"[2] = qlc:e(qlc:q([P || P <- [1,2,3], port(P)])),
             [10] = qlc:e(qlc:q([P || P <- [0,9,10,11,12],
                                      (is_reference(P) andalso P > 5)])),
             Empty = gb_sets:empty(), Single = gb_sets:singleton(42),
             GbSets = [Empty,Single],
             [Single] = qlc:e(qlc:q([S || S <- GbSets, size(S) =/= 0]))
            ">>
	 ],
    run(Config, "-import(gb_sets, [size/1]).
                 -compile({no_auto_import, [size/1, is_reference/1]}).
                 port(N) -> N rem 2 =:= 0.
                 is_reference(N) -> N rem 10 =:= 0.\n", Ts),
    ok.


%% eval/2
eval(Config) when is_list(Config) ->
    ScratchDir = filename:join([?privdir, "scratch","."]),

    Ts = [<<"{'EXIT',{badarg,_}} = (catch qlc:eval(not_a_qlc)),
             H = qlc:q([X || X <- [1,2]]),
             {'EXIT',{{unsupported_qlc_handle,{qlc_handle,foo}},_}}=
                       (catch qlc:e({qlc_handle,foo})),
             {'EXIT',{badarg,_}} = (catch qlc:eval(H, [{unique_all,badarg}])),
             {'EXIT',{badarg,_}} = 
                      (catch qlc:eval(H, [{spawn_options,badarg}])),
             {'EXIT',{badarg,_}} = 
                      (catch qlc:eval(H, [{unique_all,true},{bad,arg}])),
             {throw,t} = 
                (catch {any_term,qlc:e(qlc:q([X || X <- throw({throw,t})]))}),
             M = qlc,
             {'EXIT',{badarg,_}} = (catch M:q(bad))">>,

          [<<"Dir = \"">>,ScratchDir,<<"\",
              qlc_SUITE:prep_scratchdir(Dir),

              E = ets:new(foo, []),
              [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})],
              H = qlc:q([{X,Y} || Y <- [1,2], 
                                  X <- qlc:sort(ets:table(E),{tmpdir,Dir}),
                                  qlc_SUITE:truncate_tmpfile(Dir, 0)]),
              R = qlc:eval(H),
              ets:delete(E),
              {error,_,{bad_object,_}} = R,
              \"the tempo\" ++ _ = lists:flatten(qlc:format_error(R))">>],

          [<<"Dir = \"">>,ScratchDir,<<"\",
              qlc_SUITE:prep_scratchdir(Dir),

              E = ets:new(foo, []),
              Bin = term_to_binary(lists:seq(1,20000)),
              [true || I <- lists:seq(1, 10), not ets:insert(E, {I, I, Bin})],
              H = qlc:q([{X,Y} || Y <- [1,2], 
                                  X <- qlc:sort(ets:table(E),{tmpdir,Dir}),
                                  qlc_SUITE:crash_tmpfile(Dir, 5)]),
              R = qlc:eval(H),
              ets:delete(E),
              {error,_,{bad_object,_}} = R">>],

          <<"E = ets:new(test, []),
             H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 
                                 Y <- ets:table(E)]),
             R1 = (catch {any_term,qlc:eval(H, {unique_all,false})}),
             R2 = (catch {any_term,qlc:eval(H, {unique_all,true})}),
             ets:delete(E),
             true = {throw,bad_pre_fun} == R1,
             true = {throw,bad_pre_fun} == R2">>,

          <<"E = ets:new(test, []),
             H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 
                                 Y <- ets:table(E)]),
             R1 = (catch qlc:eval(H, {unique_all,false})),
             R2 = (catch qlc:eval(H, {unique_all,true})),
             ets:delete(E),
             {'EXIT',{bad_pre_fun,_}} = R1,
             {'EXIT',{bad_pre_fun,_}} = R2">>,

          <<"Q = qlc:q([X || X <- [4,3,2,1,0,-1], begin 3/X > 0 end]),
             {'EXIT',{badarith,_}} = (catch qlc:eval(Q, {unique_all,false})),
             {'EXIT',{badarith,_}} = (catch qlc:eval(Q, {unique_all,true}))
            ">>,

          <<"[1,2] = qlc:eval(qlc:q([X || X <- [1,2]])),
             [1,2,3,4] = qlc:eval(qlc:append([1,2],[3,4])),
             [1,2] = qlc:eval(qlc:sort([2,1])),
             E = ets:new(foo, []),
             ets:insert(E, [{1},{2}]),
             [{1},{2}] = lists:sort(qlc:eval(ets:table(E))),
             true = ets:delete(E)">>,

          <<"H = qlc:q([X || X <- [1,2], 
                             begin F = fun() -> 
                                qlc:e(qlc:q([Y || Y <- [1,2]])) end, 
                                F() == (fun f/0)() end]),
             [1,2] = qlc:e(H),
             ok.

             f() -> [1,2].
             foo() -> bar">>,

          <<"C1_0_1 = [1,2],
             %% The PT cannot rename C to C1_0_1; another name is chosen.
             [1,2] = qlc:eval(qlc:q([C || C <- C1_0_1]))">>,

          <<"H = qlc:q([X || {X,X} <- [{1,a},{2,2},{b,b},{3,4}]]),
             [2,b] = qlc:e(H),
             H1 = qlc:q([3 || {X,X} <- [{1,a},{2,2},{b,b},{3,4}]]),
             [3,3] = qlc:e(H1)">>,

          %% Just to cover a certain line in qlc.erl (avoids returning [])
          <<"E = ets:new(foo, []),
             Bin = term_to_binary(lists:seq(1,20000)),
             [true || I <- lists:seq(1, 10), not ets:insert(E, {I, I, Bin})],
             H = qlc:q([{X,Y} || Y <- [1,2], X <- qlc:sort(ets:table(E))]),
             R = qlc:eval(H),
             ets:delete(E),
             20 = length(R)">>,

          <<"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} ||
                 {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} <- 
                  [{a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w}]]),
             [_] = qlc:e(H)">>,

          <<"H = qlc:q([Y || Y <- [1,2]], 
                       {unique, begin [T] = qlc:e(qlc:q([X || X <- [true]],
                                                        cache)), 
                                      T end}),
             [1,2] = qlc:e(H)">>

          ],
    
    run(Config, Ts),
    ok.

%% cursor/2
cursor(Config) when is_list(Config) ->
    ScratchDir = filename:join([?privdir, "scratch","."]),
    Ts = [<<"{'EXIT',{badarg,_}} = 
                   (catch qlc:cursor(fun() -> not_a_cursor end)),
             H0 = qlc:q([X || X <- throw({throw,t})]),
             {throw,t} = (catch {any_term,qlc:cursor(H0)}),
             H = qlc:q([X || X <- [1,2]]),
             {'EXIT',{badarg,_}} = 
                       (catch qlc:cursor(H,{spawn_options, [a|b]})),
             {'EXIT',{badarg,_}} = 
                       (catch qlc:cursor(H,{bad_option,true}))">>,

          <<"{'EXIT',{badarg,_}} = (catch qlc:delete_cursor(not_a_cursor))">>,

          [<<"Dir = \"">>,ScratchDir,<<"\",
              qlc_SUITE:prep_scratchdir(Dir), % kludge
              E = ets:new(foo, []),
              [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})],
              H = qlc:q([{X,Y} || begin put('$qlc_tmpdir', true), true end,
                                  Y <- [1,2], 
                                  X <- qlc:sort(ets:table(E),{tmpdir,Dir}),
                                  qlc_SUITE:truncate_tmpfile(Dir, 0)]),
              C = qlc:cursor(H),
              R = qlc:next_answers(C, all_remaining),
              qlc:delete_cursor(C),
              erase('$qlc_tmpdir'),
              ets:delete(E),
              {error,_,{bad_object,_}} = R">>],

          <<"H1 = qlc:q([X || X <- [1,2]]),
             C1 = qlc:cursor(H1),
             [1,2] = qlc:next_answers(C1, all_remaining),
             [] = qlc:next_answers(C1),
             [] = qlc:next_answers(C1),
             ok = qlc:delete_cursor(C1),

             H2 = qlc:append([1,2],[3,4]),
             C2 = qlc:cursor(H2),
             [1,2,3,4] = qlc:next_answers(C2, all_remaining),
             ok = qlc:delete_cursor(C2),

             H3 = qlc:sort([2,1]),
             C3 = qlc:cursor(H3),
             [1,2] = qlc:next_answers(C3, all_remaining),
             ok = qlc:delete_cursor(C3),
             
             E = ets:new(foo, []),
             ets:insert(E, [{1},{2}]),
             H4 = ets:table(E),
             C4 = qlc:cursor(H4),
             [{1},{2}] = lists:sort(qlc:next_answers(C4, all_remaining)),
             ok = qlc:delete_cursor(C4),
             true = ets:delete(E)">>,

          <<"H = qlc:q([{X,Y} || X <- [1,2], Y <- [a,b]]),
             C = qlc:cursor(H, []),
             [{1,a},{1,b}] = qlc:next_answers(C, 2),
             [{2,a}] = qlc:next_answers(C, 1),
             [{2,b}] = qlc:next_answers(C, all_remaining),
             {'EXIT',{badarg,_}} = (catch qlc:next_answers(C, -1)),
             P = self(),
             Pid1 = spawn_link(fun() -> 
                          {'EXIT',{not_cursor_owner,_}} = 
                                (catch qlc:delete_cursor(C)),
                          P ! {self(), done} end),
             Pid2 = spawn_link(fun() -> 
                          {'EXIT',{not_cursor_owner,_}} = 
                                (catch qlc:next_answers(C)),
                          P ! {self(), done} end),
             receive {Pid1, done} -> ok end,
             receive {Pid2, done} -> ok end,
             ok = qlc:delete_cursor(C),
             {'EXIT',{badarg,_}} = (catch qlc:next_answers(not_a_cursor)),
             ok = qlc:delete_cursor(C)">>,

          <<"Q = qlc:q([X || X <- [1,2,1,2,1]]),
             C1 = qlc:cursor(Q, [{unique_all,true}]),
             [1,2] = qlc:next_answers(C1, all_remaining),
             ok = qlc:delete_cursor(C1),
             C2 = qlc:cursor(Q, [{unique_all,true}]),
             [1,2] = qlc:next_answers(C2, all_remaining),
             ok = qlc:delete_cursor(C2)">>,

          <<"Q = qlc:q([X || X <- [1,2,1,2,1]]),
             C1 = qlc:cursor(Q, [{unique_all,true},{spawn_options, []}]),
             [1,2] = qlc:next_answers(C1, all_remaining),
             ok = qlc:delete_cursor(C1),
             C2 = qlc:cursor(Q, [{unique_all,true},{spawn_options, default}]),
             [1,2] = qlc:next_answers(C2, all_remaining),
             ok = qlc:delete_cursor(C2)">>,

          <<"Q = qlc:q([X || X <- [1,2,1,2,1]]),
             C1 = qlc:cursor(Q, [{unique_all,false},{spawn_options, []}]),
             [1,2,1,2,1] = qlc:next_answers(C1, all_remaining),
             ok = qlc:delete_cursor(C1),
             C2 = qlc:cursor(Q, [{unique_all,false},{spawn_options, []}]),
             [1,2,1,2,1] = qlc:next_answers(C2, all_remaining),
             ok = qlc:delete_cursor(C2)">>,

          <<"Q = qlc:q([X || X <- [1,2,1,2,1]]),
             C1 = qlc:cursor(Q, [{unique_all,false}]),
             [1,2,1,2,1] = qlc:next_answers(C1, all_remaining),
             ok = qlc:delete_cursor(C1),
             C2 = qlc:cursor(Q, [{unique_all,false}]),
             [1,2,1,2,1] = qlc:next_answers(C2, all_remaining),
             ok = qlc:delete_cursor(C2)">>

         ],
    run(Config, Ts),
    ok.

%% fold/4
fold(Config) when is_list(Config) ->
    ScratchDir = filename:join([?privdir, "scratch","."]),
    Ts = [<<"Q = qlc:q([X || X <- [1,2,1,2,1]]),
             F = fun(Obj, A) -> A++[Obj] end,
             {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], Q, {bad,arg})),
             {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], badarg)),
             {'EXIT',{badarg,_}} = 
               (catch qlc:fold(F, [], {spawn_options, [a|b]})),
             H = qlc:q([X || X <- throw({throw,t})]),
             {throw,t} = (catch {any_term,qlc:fold(F, [], H)}),
             [1,2] = qlc:fold(F, [], Q, {unique_all,true}),
             {'EXIT',{badarg,_}} = 
               (catch qlc:fold(F, [], Q, [{unique_all,bad}])),
             [1,2,1,2,1] = 
                  qlc:fold(F, [], Q, [{unique_all,false}])">>,

          [<<"Dir = \"">>,ScratchDir,<<"\",
              qlc_SUITE:prep_scratchdir(Dir),

              E = ets:new(foo, []),
              [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})],
              H = qlc:q([{X,Y} || Y <- [1,2], 
                                  X <- qlc:sort(ets:table(E),{tmpdir,Dir}),
                                  qlc_SUITE:truncate_tmpfile(Dir, 0)]),
              F = fun(Obj, A) -> A++[Obj] end,
              R = qlc:fold(F, [], H),
              ets:delete(E),
              {error,_,{bad_object,_}} = R">>],

          <<"E = ets:new(test, []),
             H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 
                                 Y <- ets:table(E)]),
             F = fun(Obj, A) -> A++[Obj] end,
             R1 = (catch {any_term,qlc:fold(F, [], H, {unique_all,false})}),
             R2 = (catch {any_term,qlc:fold(F, [], H, {unique_all,true})}),
             ets:delete(E),
             true = {throw,bad_pre_fun} == R1,
             true = {throw,bad_pre_fun} == R2">>,

          <<"E = ets:new(test, []),
             H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 
                                 Y <- ets:table(E)]),
             F = fun(Obj, A) -> A++[Obj] end,
             R1 = (catch qlc:fold(F, [], H, {unique_all,false})),
             R2 = (catch qlc:fold(F, [], H, {unique_all,true})),
             ets:delete(E),
             {'EXIT',{bad_pre_fun,_}} = R1,
             {'EXIT',{bad_pre_fun,_}} = R2">>,

          <<"F = fun(Obj, A) -> A++[Obj] end,
             Q = qlc:q([X || X <- [1,2,1,2,1], throw({throw,wrong})]),
             {throw,wrong} = 
                 (catch {any_term,qlc:fold(F, [], Q, {unique_all,true})}),
             {throw,wrong} = 
                 (catch {any_term,qlc:fold(F, [], Q)})">>,

          <<"F = fun(Obj, A) -> A++[Obj] end,
             Q = qlc:q([X || X <- [4,3,2,1,0,-1], begin 3/X > 0 end]),
             {'EXIT',{badarith,_}} = 
                       (catch qlc:fold(F, [], Q, {unique_all,true})),
             {'EXIT',{badarith,_}} = 
                 (catch qlc:fold(F, [], Q, [{unique_all,false}]))
            ">>,

          <<"F = fun(Obj, A) -> A++[Obj] end,
             [1,2] = qlc:fold(F, [], qlc:q([X || X <- [1,2]])),
             [1,2,3,4] = qlc:fold(F, [], qlc:append([1,2],[3,4])),
             [1,2] = qlc:fold(F, [], qlc:sort([2,1])),
             E = ets:new(foo, []),
             ets:insert(E, [{1},{2}]),
             [{1},{2}] = lists:sort(qlc:fold(F, [], ets:table(E))),
             true = ets:delete(E)">>,

          <<"F = fun(_Obj, _A) -> throw({throw,fatal}) end,
             Q = qlc:q([X || X <- [4,3,2]]),
             {throw,fatal} = 
              (catch {any_term,qlc:fold(F, [], Q, {unique_all,true})}),
             {throw,fatal} = 
               (catch {any_term,qlc:fold(F, [], Q, [{unique_all,false}])})">>,

          <<"G = fun(_Obj, _A, D) -> 17/D  end,
             F = fun(Obj, A) -> G(Obj, A, 0) end,
             Q = qlc:q([X || X <- [4,3,2]]),
             {'EXIT',{badarith,_}} = 
                    (catch qlc:fold(F, [], Q, {unique_all,true})),
             {'EXIT',{badarith,_}} = 
               (catch qlc:fold(F, [], Q, [{unique_all,false}]))
            ">>
         ],
    run(Config, Ts),
    ok.

%% Test the unique_all option of eval.
eval_unique(Config) when is_list(Config) ->
    Ts = [<<"QLC1 = qlc:q([X || X <- qlc:append([[1,1,2], [1,2,3,2,3]])]),
             [1,2,3] = qlc:eval(QLC1, {unique_all,true}),
             QLC2 = qlc:q([X || X <- [1,2,1,2,1,2,1]]),
             [1,2] = qlc:e(QLC2, {unique_all,true})">>,

          <<"E = ets:new(test, []),
             true = ets:insert(E, [{1,a},{2,b},{3,c}]),
             H = qlc:q([X || X <- qlc:append([ets:table(E), ets:table(E)])]),
             R1 = qlc:e(H, {unique_all,false}),
             R2 = qlc:e(H, {unique_all,true}),
             ets:delete(E),
             true = lists:sort(R1) == [{1,a},{1,a},{2,b},{2,b},{3,c},{3,c}],
             true = lists:sort(R2) == [{1,a},{2,b},{3,c}]
            ">>,

          <<"Q1 = qlc:q([{X,make_ref()} || X <- [1,2,1,2]]),
             [_,_] = qlc:e(Q1, {unique_all,true}),
             [_,_,_,_] = qlc:e(Q1, {unique_all,false}),
             [_,_] = qlc:e(Q1, [{unique_all,true}]),
             Q2 = qlc:q([{X,make_ref()} || X <- qlc:append([[1,2,1,2]])]),
             [_,_] = qlc:e(Q2, {unique_all,true}),
             [_,_,_,_] = qlc:e(Q2, {unique_all,false}),
             [_,_] = qlc:e(Q2, [{unique_all,true}])
            ">>,

          <<"Q = qlc:q([{X,make_ref()} || X <- qlc:sort([1,2,1,2])]),
             [_, _] = qlc:e(Q, {unique_all,true}),
             Q1 = qlc:q([X || X <- [1,2,1,2]]),
             Q2 = qlc:q([{X,make_ref()} || X <- qlc:sort(Q1)]),
             [_, _] = qlc:e(Q2, {unique_all,true})
            ">>,

          <<"E = ets:new(test, []),
             true = ets:insert(E, [{1,a},{2,b},{3,c}]),
             H = qlc:q([X || X <- qlc:append([[1,2,1,2]])]),
             [1,2,1,2] = qlc:e(H, {unique_all,false}),
             [1,2] = qlc:e(H, {unique_all,true}),
             ets:delete(E)">>,

          <<"E = ets:new(foo, [duplicate_bag]),
             true = ets:insert(E, [{1,a},{1,a},{2,b},{3,c},{4,c},{4,d}]),
             Q1 = qlc:q([{X,make_ref()} || {_, X} <- ets:table(E)]),
             true = length(qlc:eval(Q1, {unique_all, true})) =:= 5,
             Q2 = qlc:q([X || {_, X} <- ets:table(E)]),
             true = length(qlc:eval(Q2, {unique_all, true})) =:= 4,
             Q3 = qlc:q([element(2, X) || X <- ets:table(E)]),
             true = length(qlc:eval(Q3, {unique_all, true})) =:= 4,
             Q4 = qlc:q([1 || _X <- ets:table(E)]),
             true = length(qlc:eval(Q4, {unique_all, true})) =:= 1,
             true = ets:delete(E)
            ">>,

          <<"Q1 = qlc:q([X || X <- qlc:append([[1], [2,1]])]),
             Q2 = qlc:q([X || X <- qlc:append([[2,1], [2]])]),
             Q3 = qlc:q([{X,Y} || X <- Q1, Y <- Q2]),
             [{1,2},{1,1},{2,2},{2,1}] = qlc:e(Q3, {unique_all,true}),
             Q4 = qlc:q([{X,Y,make_ref()} || X <- Q1, Y <- Q2]),
             [{1,2,_},{1,1,_},{2,2,_},{2,1,_}] = qlc:e(Q4, {unique_all,true})
            ">>,

          <<"Q1 = qlc:q([X || X <- [1,2,1]]),
             Q2 = qlc:q([X || X <- [2,1,2]]),
             Q3 = qlc:q([{X,Y} || X <- Q1, Y <- Q2]),
             [{1,2},{1,1},{2,2},{2,1}] = qlc:e(Q3,{unique_all,true}),
             Q4 = qlc:q([{X,Y,make_ref()} || X <- Q1, Y <- Q2]),
             [{1,2,_},{1,1,_},{2,2,_},{2,1,_}] = qlc:e(Q4, {unique_all,true})
            ">>,

          <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{1,b}]]),
             [1] = qlc:e(Q1, {unique_all, true}),
             Q2 = qlc:q([a || _ <- [{1,a},{1,b}]]),
             [a] = qlc:e(Q2, {unique_all, true})
            ">>,

          <<"Q = qlc:q([SQV || SQV <- qlc:q([X || X <- [1,2,1,#{a => 1}]],
                                             unique)],
                       unique),
             {call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} =
                 qlc:info(Q, [{format,abstract_code},unique_all]),
             [1,2,#{a := 1}] = qlc:e(Q)">>,

          <<"Q = qlc:q([X || X <- [1,2,1]]),
             {call,_,_,[{lc,_,{var,_,'X'},[{generate,_,{var,_,'X'},_}]},_]} =
                 qlc:info(Q, [{format,abstract_code},unique_all]),
             [1,2] = qlc:e(Q, unique_all)">>,

          <<"Q1 = qlc:sort([{1},{2},{3},{1}], [{unique,true}]),
             Q = qlc:sort(Q1,[{unique,true}]),
             {sort,{sort,{list,_},[{unique,true}]},[]} = i(Q)">>

         ],
    run(Config, Ts),
    ok.

%% Test the cache_all and unique_all options of eval.
eval_cache(Config) when is_list(Config) ->
    Ts = [
       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H = qlc:q([X || Y <- [3,4], 
                          ets:insert(E, {Y}),
                          X <- ets:table(E)]), % already unique, no cache...
          {qlc, _,
           [{generate, _, {qlc, _,
                           [{generate, _, {list, [3,4]}}],
                           [{unique,true}]}}, 
            _,
            {generate, _, {table,_}}],
           [{unique,true}]} = i(H, [cache_all, unique_all]),
          [{1},{2},{3},{4}] = qlc:e(H,  [cache_all, unique_all]),
          ets:delete(E)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H = qlc:q([X || Y <- [3,4], 
                          ets:insert(E, {Y}),
                          X <- ets:table(E)]), % no cache...
          {qlc, _,
           [{generate, _,{list, [3,4]}}, 
            _,
            {generate, _, {table,_}}],
           []} = i(H, cache_all),
          [{1},{2},{3},{1},{2},{3},{4}] = qlc:e(H,  [cache_all]),
          ets:delete(E)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H = qlc:q([X || Y <- [3,4], 
                          ets:insert(E, {Y}),
                          X <- qlc:q([X || X <- ets:table(E)], cache)]),
          {qlc, _,
           [{generate, _, {list, [3,4]}}, 
            _,
            {generate, _, {qlc, _,
                           [{generate, _, {table,_}}],
                           [{cache,ets}]}}],
           []} = i(H, cache_all),
          [{1},{2},{3},{1},{2},{3}] = qlc:e(H,  [cache_all]),
          ets:delete(E)">>,

       <<"%% {cache_all,no} does not override {cache,true}.
          E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H = qlc:q([X || Y <- [3,4], 
                          ets:insert(E, {Y}),
                          X <- qlc:q([X || X <- ets:table(E)], cache)]),
          {qlc, _,
           [{generate, _, {list, [3,4]}}, 
            _,
            {generate, _, {qlc, _,
                           [{generate, _, {table,_}}],
                           [{cache,ets}]}}],
           []} = i(H, {cache_all,no}),
          [{1},{2},{3},{1},{2},{3}] = qlc:e(H,  [cache_all]),
          ets:delete(E)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H = qlc:q([X || Y <- [3,4], 
                          ets:insert(E, {Y}),
                          X <- ets:table(E)]),
          {qlc, _,
           [{generate, _, {qlc, _, [{generate, _,{list, [3,4]}}],
                           [{unique,true}]}}, 
            _,
            {generate, _,{table,_}}],
           [{unique,true}]} = i(H, unique_all),
          [{1},{2},{3},{4}] = qlc:e(H, [unique_all]),
          ets:delete(E)">>,

          %% cache_all is ignored 
       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2},{0}]),
          H = qlc:q([X || X <- qlc:sort(ets:table(E))]),
          {sort,_Table,[]} = i(H, cache_all),
          [{0},{1},{2}] = qlc:e(H, cache_all),
          ets:delete(E)">>,

       <<"F = fun(Obj, A) -> A++[Obj] end,
          E = ets:new(apa, [duplicate_bag]),
          true = ets:insert(E, [{1,a},{2,b},{1,a}]),
          Q = qlc:q([X || X <- ets:table(E)], cache),
          {table, _} = i(Q, []),
          R = qlc:fold(F, [], Q, []),
          ets:delete(E),
          true = [{1,a},{1,a},{2,b}] == lists:sort(R)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2},{0}]),
          H = qlc:q([X || X <- ets:table(E)], cache),
          {table, _} = i(H, cache_all),
          [{0},{1},{2}]= qlc:e(H, cache_all),
          ets:delete(E)">>,

       <<"E = ets:new(foo, []),
          true = ets:insert(E, [{1}, {2}]),
          Q1 = qlc:q([{X} || X <- ets:table(E)]),
          Q2 = qlc:q([{X,Y} || {X} <- Q1, {Y} <- Q1]),
          {qlc, _, [{generate, _, {table, _}},
                    {generate, _, {qlc, _, [{generate, _, {table, _}}],
                                   [{cache,ets}]}}],
           []} = i(Q2, cache_all),
          [{{1},{1}},{{1},{2}},{{2},{1}},{{2},{2}}] = 
              lists:sort(qlc:e(Q2, cache_all)),
          ets:delete(E)">>,

       <<"L1 = [1,2,3], 
          L2 = [4,5,6],
          Q1 = qlc:append(L1, L2),
          Q2 = qlc:q([{X} || X <- Q1]),
          {qlc, _,[{generate, _,{append, [{list, L1}, {list, L2}]}}], []} = 
              i(Q2, [cache_all]),
          [{1},{2},{3},{4},{5},{6}] = qlc:e(Q2, [cache_all])">>,

       <<"H = qlc:sort(qlc:q([1 || _ <- [a,b]])),
          {sort, {qlc, _, [{generate, _, {qlc, _, [{generate, _, 
                                                    {list, [a,b]}}],
                                          [{unique,true}]}}],
                  [{unique,true}]},
                 []} = i(H, unique_all),
          [1] = qlc:e(H, unique_all)">>

         ],
    run(Config, Ts),
    ok.

%% Test the append function.
append(Config) when is_list(Config) ->
    Ts = [<<"C = qlc:cursor(qlc:q([X || X <- [0,1,2,3], begin 10/X > 0.0 end])),
             R = (catch qlc:next_answers(C)),
             {'EXIT',{badarith,_}} = R">>,

          <<"C = qlc:cursor(qlc:q([X || X <- [0 | fun() -> exit(bad) end]])),
             R = (catch qlc:next_answers(C)),
             {'EXIT',bad} = R">>,

          <<"{'EXIT',{badarg,_}} = (catch qlc:append([a], a)),
             {'EXIT',{badarg,_}} = (catch qlc:append([[a],a]))">>,

          <<"C = qlc:cursor(qlc:q([X || X <- [0,1,2,3], 
                                     begin throw({throw,wrong}), true end])),
             {throw,wrong} = (catch {any_term,qlc:next_answers(C)})">>,

          <<"QLC = qlc:q([X || X <- [0,1,2,3], 
                               begin throw({throw,wrong}), true end]),
             {throw,wrong} = (catch {any_term,qlc:eval(QLC)}),
             {throw,wrong} = 
                 (catch {any_term,qlc:e(QLC, {unique_all,true})})">>,

          <<"H1 = qlc:q([X || X <- [1,2,3]]),
             H2 = qlc:q([X || X <- [4,5,6]]),
             R = qlc:e(qlc:q([X || X <- qlc:append([H1, H2])])),
             true = R == [1,2,3,4,5,6]">>,

          <<"H1 = [1,2,3],
             H2 = qlc:q([X || X <- [4,5,6]]),
             R = qlc:e(qlc:q([X || X <- qlc:append(H1, H2)])),
             true = R == [1,2,3,4,5,6]">>,

          <<"H1 = qlc:q([X || X <- [1,2,3]]),
             H2 = qlc:q([X || X <- [4,5,6]]),
             R = qlc:e(qlc:q([X || X <- qlc:append(qlc:e(H1), H2)])),
             true = R == [1,2,3,4,5,6]">>,

          <<"H1 = qlc:q([X || X <- [1,2,3]]),
             H2 = [4,5,6],
             R = qlc:e(qlc:q([X || X <- qlc:append(H1, H2)])),
             true = R == [1,2,3,4,5,6]">>,

          <<"H1 = qlc:q([X || X <- [1,2,3]]),
             H2 = qlc:q([X || X <- [4,5,6]]),
             R = qlc:e(qlc:q([X || X <- qlc:append([H1, H2, H1]), X < 5])),
             true = R == [1,2,3,4,1,2,3]">>,

          <<"R = qlc:e(qlc:q([X || X <- qlc:append([lista(), anrop()])])),
             true = R == [a,b,1,2],
             ok.

             lista() ->
                 [a,b].

             anrop() ->
                  qlc:q([X || X <- [1,2]]).
             foo() -> bar">>,

          %% Used to work up to R11B.
          %% <<"apa = qlc:e(qlc:q([X || X <- qlc:append([[1,2,3], ugly()])])),
          %%   ok.
          %%
          %%   ugly() ->
          %%       [a | apa].
          %%   foo() -> bar">>,

          
          %% Maybe this one should fail.
          <<"[a|b] = qlc:e(qlc:q([X || X <- qlc:append([[a|b]])])),
             ok">>,

          <<"17 = qlc:e(qlc:q([X || X <- qlc:append([[1,2,3],ugly2()])])),
             ok.

             ugly2() ->
                 [a | fun() -> 17 end].
             foo() -> bar">>,

          <<"E = ets:new(test, []),
             true = ets:insert(E, [{1,a},{2,b},{3,c}]),
             H = qlc:q([X || X <- qlc:append([ets:table(E), apa])]),
             {'EXIT',{badarg,_}} = (catch qlc:e(H)),
             false = ets:info(E, safe_fixed),
             {'EXIT',{badarg,_}} = (catch qlc:e(H)),
             false = ets:info(E, safe_fixed),
             {'EXIT',{badarg,_}} = (catch qlc:cursor(H)),
             false = ets:info(E, safe_fixed),
             F = fun(Obj, A) -> A++[Obj] end,
             {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], H)),
             false = ets:info(E, safe_fixed),
             ets:delete(E)">>,

          <<"H1 = qlc:q([X || X <- [1,2,3]]),
             H2 = qlc:q([X || X <- [a,b,c]]),
             R = qlc:e(qlc:q([X || X <- qlc:append(H1,qlc:append(H1,H2))])),
             true = R == [1,2,3,1,2,3,a,b,c]">>,

          <<"H = qlc:q([X || X <- qlc:append([],qlc:append([[], []]))]),
             [] = qlc:e(H)">>,

          <<"Q1 = qlc:q([X || X <- [3,4,4]]),
             Q2 = qlc:q([X || X <- qlc:sort(qlc:append([[1,2], Q1]))]),
             [1,2,3,4,4] = qlc:e(Q2),
             [1,2,3,4] = qlc:e(Q2, {unique_all,true})">>,

          <<"[] = qlc:e(qlc:q([X || X <- qlc:append([])]))">>,

          <<"Q1 = qlc:q([X || X <- [a,b]]),
             Q2 = qlc:q([X || X <- [1,2]]),
             Q3 = qlc:append([Q1, Q2, qlc:sort([2,1])]),
             Q = qlc:q([X || X <- Q3]),
             {append, [{list, [a,b]}, 
                       {list, [1,2]}, 
                       {sort,{list, [2,1]},[]}]} = i(Q),
             [a,b,1,2,1,2] = qlc:e(Q)">>

          ],
    run(Config, Ts),
    ok.

%% Simple call from evaluator.
evaluator(Config) when is_list(Config) ->
    true = is_alive(),
    evaluator_2(Config, []),
    {ok, Node} = start_node(qlc_SUITE_evaluator),
    ok = rpc:call(Node, ?MODULE, evaluator_2, [Config, [compiler]]),
    test_server:stop_node(Node),
    ok.

evaluator_2(Config, Apps) ->
    lists:foreach(fun(App) -> true = code:del_path(App) end, Apps),
    FileName = filename:join(?privdir, "eval"),
    ok = file:write_file(FileName,
                         <<"H = qlc:q([X || X <- L]),
                            [1,2,3] = qlc:e(H).">>),
    Bs = erl_eval:add_binding('L', [1,2,3], erl_eval:new_bindings()),
    ok = file:eval(FileName, Bs),

    %% The error message is "handled" a bit too much... 
    %% (no trace of erl_lint left)
    ok = file:write_file(FileName,
             <<"H = qlc:q([X || X <- L]), qlc:e(H).">>),
    {error,_} = file:eval(FileName),

    %% Ugly error message; badarg is caught by file.erl.
    ok = file:write_file(FileName,
             <<"H = qlc:q([Z || {X,Y} <- [{a,2}], Z <- [Y]]), qlc:e(H).">>),
    {error,_} = file:eval(FileName),

    _ = file:delete(FileName),
    ok.

start_node(Name) ->
    PA = filename:dirname(code:which(?MODULE)),
    test_server:start_node(Name, slave, [{args, "-pa " ++ PA}]).

%% string_to_handle/1,2.
string_to_handle(Config) when is_list(Config) ->
    {'EXIT',{badarg,_}} = (catch qlc:string_to_handle(14)),
    {'EXIT',{badarg,_}} =
        (catch qlc:string_to_handle("[X || X <- [a].", unique_all)),
    R1 = {error, _, {_,erl_scan,_}} = qlc:string_to_handle("'"),
    "1: unterminated " ++ _ = lists:flatten(qlc:format_error(R1)),
    {error, _, {_,erl_parse,_}} = qlc:string_to_handle("foo"),
    {'EXIT',{badarg,_}} = (catch qlc:string_to_handle("foo, bar.")),
    R3 = {error, _, {_,?QLC,not_a_query_list_comprehension}} =
        qlc:string_to_handle("bad."),
    "1: argument is not" ++ _ = lists:flatten(qlc:format_error(R3)),
    R4 = {error, _, {_,?QLC,{used_generator_variable,'Y'}}} =
        qlc:string_to_handle("[X || begin Y = [1,2], true end, X <- Y]."),
    "1: generated variable 'Y'" ++ _ =
        lists:flatten(qlc:format_error(R4)),
    {error, _, {_,erl_lint,_}} = qlc:string_to_handle("[X || X <- A]."),
    H1 = qlc:string_to_handle("[X || X <- [1,2]]."),
    [1,2] = qlc:e(H1),
    H2 = qlc:string_to_handle("[X || X <- qlc:append([a,b],"
                                    "qlc:e(qlc:q([X || X <- [c,d,e]])))]."),
    [a,b,c,d,e] = qlc:e(H2),
    %% The generated fun has many arguments (erl_eval has a maximum of 20).
    H3 = qlc:string_to_handle(
           "[{A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} ||"
           " {A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W} <- []]."),
    [] = qlc:e(H3),
    Bs1 = erl_eval:add_binding('L', [1,2,3], erl_eval:new_bindings()),
    H4 = qlc:string_to_handle("[X || X <- L].", [], Bs1),
    [1,2,3] = qlc:e(H4),
    H5 = qlc:string_to_handle("[X || X <- [1,2,1,2]].", [unique, cache]),
    [1,2] = qlc:e(H5),

    Ets = ets:new(test, []),
    true = ets:insert(Ets, [{1}]),
    Bs2 = erl_eval:add_binding('E', Ets, erl_eval:new_bindings()),
    Q = "[X || {X} <- ets:table(E)].",
    [1] = qlc:e(qlc:string_to_handle(Q, [], Bs2)),
    [1] = qlc:e(qlc:string_to_handle(Q, {max_lookup,1000}, Bs2)),
    [1] = qlc:e(qlc:string_to_handle(Q, {max_lookup,infinity}, Bs2)),
    {'EXIT',{badarg,_}} =
        (catch qlc:string_to_handle(Q, {max_lookup,-1}, Bs2)),
    {'EXIT', {no_lookup_to_carry_out, _}} =
        (catch qlc:e(qlc:string_to_handle(Q, {lookup,true}, Bs2))),
    ets:delete(Ets),

    %% References can be scanned and parsed.
    E2 = ets:new(test, [bag]),
    Ref = make_ref(),
    true = ets:insert(E2, [{Ref,Ref}]),
    S2 = "[{Val1} || {Ref1, Val1} <- ets:table("++io_lib:write(E2)++"),"
         "Ref1 =:= Ref].",
    Bs = erl_eval:add_binding('Ref', Ref, erl_eval:new_bindings()),
    [{Ref}] = qlc:e(qlc:string_to_handle(S2, [], Bs)),
    ets:delete(E2),

    ok.

%% table
table(Config) when is_list(Config) ->
    dets:start(),
    Ts = [
       <<"E = ets:new(test, []),
          {'EXIT',{badarg,_}} = 
               (catch qlc:e(qlc:q([X || X <- ets:table(E, [badarg])]))),
          [] = qlc:e(qlc:q([X || X <- ets:table(E)])),
          ets:delete(E)">>,

       <<"{'EXIT',{badarg,_}} = (catch qlc:table(not_a_fun, []))">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([{X,Y} || X <- ets:table(E), Y <- ets:table(E)]),
          R = qlc:e(H),
          ets:delete(E),
          [{{1,a},{1,a}},{{1,a},{2,b}},{{1,a},{3,c}},
           {{2,b},{1,a}},{{2,b},{2,b}},{{2,b},{3,c}},

           {{3,c},{1,a}},{{3,c},{2,b}},{{3,c},{3,c}}] = lists:sort(R)">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([X || X <- qlc:append([ets:table(E), [a,b,c], 
                                           ets:table(E)])]),
          R = qlc:e(H),
          ets:delete(E),
          [a,b,c,{1,a},{1,a},{2,b},{2,b},{3,c},{3,c}] = lists:sort(R)">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          false = ets:info(E, safe_fixed),
          H = qlc:q([{X,Y} || X <- ets:table(E, {n_objects, default}), 
                              Y <- ets:table(E, {n_objects, 2}), 
                              false =/= ets:info(E, safe_fixed), 
                              throw({throw,apa})]),
          {throw,apa} = (catch {any_term,qlc:e(H)}),
          false = ets:info(E, safe_fixed),
          ets:delete(E)">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          false = ets:info(E, safe_fixed),
          H = qlc:q([{X,Y} || X <- ets:table(E), Y <- ets:table(E), 
                              false =/= ets:info(E, safe_fixed), exit(apa)]),
          {'EXIT',apa} = (catch {any_term,qlc:e(H)}),
          false = ets:info(E, safe_fixed),
          ets:delete(E)">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_throw(E), 
                              Y <- ets:table(E)]),
          R = (catch {any_term,qlc:cursor(H)}),
          false = ets:info(E, safe_fixed),
          ets:delete(E),
          {throw,bad_pre_fun} = R">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([{X,Y} || X <- qlc_SUITE:bad_table_exit(E), 
                              Y <- ets:table(E)]),
          R = (catch {any_term,qlc:cursor(H)}),
          false = ets:info(E, safe_fixed),
          ets:delete(E),
          {'EXIT',{bad_pre_fun,_}} = R">>,

       <<"E = ets:new(test, [ordered_set]),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([X || X <- qlc_SUITE:default_table(E)]),
          R = qlc:e(H),
          ets:delete(E),
          [{1,a},{2,b},{3,c}] = R">>,

       <<"E = ets:new(test, [ordered_set]),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([X || X <- qlc_SUITE:bad_table(E)]),
          {'EXIT', {badarg, _}} = (catch qlc:e(H)),
          ets:delete(E)">>,

       %% The info tag num_of_objects is currently not used.
%%        <<"E = ets:new(test, [ordered_set]),
%%           true = ets:insert(E, [{1,a},{2,b},{3,c}]),
%%           H = qlc:q([X || X <- qlc_SUITE:bad_table_info_fun_n_objects(E)]),
%%           {'EXIT', finito} = (catch {any_term,qlc:e(H)}),
%%           ets:delete(E)">>,

       <<"E = ets:new(test, [ordered_set]),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([Y || {X,Y} <- qlc_SUITE:bad_table_info_fun_indices(E),
                          X =:= a]),
          %% This is due to lookup. If the table were traversed there
          %% would be no failure.
          {throw, apa} = (catch {any_term,qlc:e(H)}),
          ets:delete(E)">>,

       <<"E = ets:new(test, [ordered_set]),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          H = qlc:q([Y || {X,Y} <- qlc_SUITE:bad_table_info_fun_keypos(E),
                          X =:= a]),
          {'EXIT',{keypos,_}} = (catch {any_term,qlc:info(H)}),
          {'EXIT',{keypos,_}} = (catch {any_term,qlc:e(H)}),
          ets:delete(E)">>,

       begin
       MS = ets:fun2ms(fun(X) when element(1, X) > 1 -> X end),
       [<<"E = ets:new(test, []),
           true = ets:insert(E, [{1,a},{2,b},{3,c}]),
           MS = ">>, io_lib:format("~w", [MS]), <<",
           H = qlc:q([{X,Y} || X <- ets:table(E,{traverse,{select, MS}}), 
                               Y <- ets:table(E)]),
           R = qlc:e(H),
           ets:delete(E),
           [{{2,b},{1,a}},{{2,b},{2,b}},{{2,b},{3,c}},
            {{3,c},{1,a}},{{3,c},{2,b}},{{3,c},{3,c}}] = lists:sort(R)">>]
       end,

       begin % a short table
       MS = ets:fun2ms(fun(X) when element(1, X) > 1 -> X end),
       [<<"E = ets:new(test, []),
           true = ets:insert(E, [{0,b}]),
           MS =  ">>, io_lib:format("~w", [MS]), <<",
           H1 = qlc:q([X || X <- ets:table(E)]),
           R1 = qlc:e(H1),
           H2 = qlc:q([X || X <- ets:table(E, {traverse, {select, MS}})]),
           R2 = qlc:e(H2),
           ets:delete(E),
           [_] = R1,
           [] = R2">>]
       end,

       begin
       File = filename:join(?privdir, "detsfile"),
       _ = file:delete(File),
       [<<"{ok, Tab} = dets:open_file(apa, [{file,\"">>, File, <<"\"}, 
                                           {type,bag}]),
           ok = dets:insert(Tab, [{1,a},{1,b}]),
           R = qlc:e(qlc:q([X || X <- dets:table(Tab)])),
           dets:close(Tab),
           file:delete(\"">>, File, <<"\"),
           R">>]
       end,

       %% [T || P <- Table, F] turned into a match spec.
       <<"E = ets:new(apa, [duplicate_bag]),
          true = ets:insert(E, [{1,a},{2,b},{3,c},{4,d}]),
          QH = qlc:q([X || {X,_} <- ets:table(E), X > 2], unique),
          {qlc, _, [{generate, _, {table, _}}], [{unique,true}]} = i(QH),
          [3,4] = lists:sort(qlc:e(QH)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_format(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_format_arity(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_info_arity(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_traverse(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_post(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_max_lookup(E)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, []),
          true = ets:insert(E, [{1,a},{2,b}]),
          {'EXIT', {badarg, _}} = (catch qlc_SUITE:bad_table_lookup(E)),
          ets:delete(E)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(2, X) || X <- qlc_SUITE:table(L, [2]),
                                       (element(1, X) =:= 1)
                                        or (2 =:= element(1, X))]),
          [a,b] = lists:sort(qlc:e(QH))">>,

       <<"etsc(fun(E) ->
              Q = qlc:q([{A,B} || {A,B} <- 
                                qlc:q([{B,A} || {A,B} <- ets:table(E), 
                                                (A =:= 1) or (A =:= 2),
                                                math:sqrt(B) < A])]),
              [{2,2}] = qlc:eval(Q),
              [1,2] = lookup_keys(Q)
           end, [{1,1},{2,2}])">>
       ],
    run(Config, Ts),

    Ts2 = [
       %% [T || P <- Table, F] turned into a match spec. Records needed.
       <<"E = ets:new(foo, [bag]),
          ets:insert(E, [{a,1,2},#a{b=3,c=4},{a,3}]),
          QH = qlc:q([X || X <- ets:table(E), is_record(X, a)]),
          {list,{table,_}, _} = i(QH),
          [{a,1,2},{a,3,4}] = lists:sort(qlc:eval(QH)),
          ets:delete(E)">>
       ],
    run(Config, <<"-record(a, {b,c}).\n">>, Ts2),

    ok.

%% Caller or cursor process dies.
process_dies(Config) when is_list(Config) ->
    Ts = [
       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          false = ets:info(E, safe_fixed),
          Parent = self(),
          F = fun() -> 
                      H = qlc:q([X || X <- ets:table(E)]),
                      qlc:cursor(H),
                      Parent ! {self(),ok}                
              end,
          Pid = spawn_link(F),
          receive {Pid,ok} -> ok end,
          timer:sleep(10),
          false = ets:info(E, safe_fixed),    
          ets:delete(E)">>,

       <<"%% This is not nice. The cursor's monitor kicks in.
          E = ets:new(test, []),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          false = ets:info(E, safe_fixed),
          Parent = self(),
          F = fun() -> 
                      H = qlc:q([X || begin
                                          process_flag(trap_exit, false),
                                          {links, [Pid]} = 
                                              process_info(self(), links),
                                          unlink(Pid),
                                          timer:sleep(1),
                                          {links, []} =
                                              process_info(self(), links),
                                          true 
                                      end, 
                                      X <- ets:table(E)]),
                      C = qlc:cursor(H),
                      qlc:next_answers(C),
                      Parent ! {self(),ok}                
              end,
          Pid = spawn_link(F),
          receive {Pid,ok} -> ok end,
          timer:sleep(10),
          false = ets:info(E, safe_fixed),    
          ets:delete(E)">>,

       <<"H = qlc:q([X || X <- [1,2]]),
          {qlc_cursor, Term} = C = qlc:cursor(H),
          PF = process_flag(trap_exit, true),
          F = fun(T) -> not is_pid(T) end,
          [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)),
          exit(Pid, kill),
          timer:sleep(1),
          {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} =
                (catch qlc:next_answers(C)),
          process_flag(trap_exit, PF)">>,
       <<"H = qlc:q([X || begin process_flag(trap_exit, true), true end, 
                          X <- [1,2]]),
          {qlc_cursor, Term} = C = qlc:cursor(H),
          PF = process_flag(trap_exit, true),
          F = fun(T) -> not is_pid(T) end,
          [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)),
          [1] = qlc:next_answers(C, 1),
          exit(Pid, stop),
          timer:sleep(1),
          {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} =
              (catch qlc:next_answers(C)),
          process_flag(trap_exit, PF)">>,
       <<"%% This is not nice. No cleanup is done...
          H = qlc:q([X || begin process_flag(trap_exit, false), true end, 
                     X <- [1,2]]),
          {qlc_cursor, Term} = C = qlc:cursor(H),
          PF = process_flag(trap_exit, true),
          F = fun(T) -> not is_pid(T) end,
          [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)),
          [1] = qlc:next_answers(C, 1),
          exit(Pid, stop),
          timer:sleep(1),
          {'EXIT', {{qlc_cursor_pid_no_longer_exists, Pid}, _}} =
              (catch qlc:next_answers(C)),
          process_flag(trap_exit, PF)">>,

       <<"PF = process_flag(trap_exit, true),
          E = ets:new(test, []),
          %% Hard kill. No cleanup will be done.
          H = qlc:q([X || begin exit(self(), kill), true end, 
                          X <- ets:table(E)]),
          C = qlc:cursor(H),
          {'EXIT', {{qlc_cursor_pid_no_longer_exists, _}, _}} =
               (catch qlc:next_answers(C)),
          false = ets:info(E, safe_fixed), % - but Ets cleans up anyway.
          true = ets:delete(E),
          process_flag(trap_exit, PF)">>,

       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a}]),
          %% The signal is caught by trap_exit. No process dies...
          H = qlc:q([X || begin exit(self(), normal), true end, 
                          X <- ets:table(E)]),
          C = qlc:cursor(H, {spawn_options, []}),
          [{1,a}] = qlc:next_answers(C),
          qlc:delete_cursor(C),
          false = ets:info(E, safe_fixed),
          true = ets:delete(E)">>,
       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a}]),
          %% The same as last example.
          H = qlc:q([X || begin 
                              process_flag(trap_exit, true), 
                              exit(self(), normal), true 
                          end, 
                          X <- ets:table(E)]),
          C = qlc:cursor(H, {spawn_options, []}),
          [{1,a}] = qlc:next_answers(C),
          qlc:delete_cursor(C),
          false = ets:info(E, safe_fixed),
          true = ets:delete(E), ok">>,
       <<"E = ets:new(test, []),
          true = ets:insert(E, [{1,a}]),
          H = qlc:q([X || X <- ets:table(E)]),
          SpawnOpts = [link, monitor], % monitor is ignored
          {qlc_cursor, Term} = C = qlc:cursor(H, {spawn_options, SpawnOpts}),
          F = fun(T) -> not is_pid(T) end,
          [Pid|_] = lists:dropwhile(F, tuple_to_list(Term)),
          Me = self(),
          qlc_SUITE:install_error_logger(),
          Tuple = {this, tuple, is, writton, onto, the, error_logger},
          SP = spawn(fun() ->
                  Pid ! Tuple,
                  Me ! {self(), done}
                end),
          receive {SP, done} -> ok end,
          [{1,a}] = qlc:next_answers(C),
          qlc:delete_cursor(C),
          {error, _Pid, Tuple} = qlc_SUITE:read_error_logger(),
          qlc_SUITE:uninstall_error_logger(),
          false = ets:info(E, safe_fixed),
          true = ets:delete(E), ok">>

        ],
    run(Config, Ts),
    ok.

%% The sort option.
sort(Config) when is_list(Config) ->
    Ts = [
       <<"H = qlc:q([X || X <- qlc:sort([1,2,3,2], {unique,true})]),
          [1,2,3] = qlc:e(H),
          C1 = qlc:cursor(H),
          [1,2,3] = qlc:next_answers(C1, all_remaining),
          qlc:delete_cursor(C1)">>,

       <<"H = qlc:q([{X,Y} || X <- qlc:sort(qlc:q([X || X <- [1,2,3,2]]), 
                                            {unique,true}),
                              Y <- [a,b]]),
          [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H),
          C = qlc:cursor(H),
          [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 
              qlc:next_answers(C, all_remaining),
          qlc:delete_cursor(C)">>,

       <<"H = qlc:q([X || X <- qlc:sort(qlc:q([X || X <- apa]))]),
          {'EXIT',{badarg,_}} = (catch qlc:e(H))">>,

          %% An example with a side effect. The result may vary...
       <<"E = ets:new(test, [duplicate_bag]),
          true = ets:insert(E, [{1,17},{1,a}]),
          H_1 = qlc:q([X || X <- ets:table(E)]),
          H = qlc:q([X || X <- [1,2,3], ets:insert(E, {1,-X}), 
                          {_,Y} <- H_1, 
                          X > Y]),
          true = lists:sort(qlc:e(H)) == [1,2,2,3,3,3],
          true = ets:delete(E)">>,

       <<"E = ets:new(test, [duplicate_bag]),
          true = ets:insert(E, [{1,17}]),
          H_1 = qlc:q([X || X <- qlc:sort(ets:tab2list(E))]),
          %% Note: side effect in filter!
          H = qlc:q([X || X <- [1,2,3], ets:insert(E, {1,-X}),
                          {_,Y} <- H_1, X > Y]),
          [] = qlc:e(H),
          true = ets:delete(E)">>,

       <<"H = qlc:q([X || X <- qlc:sort([1,2,3], {fopp,la})]),
          {'EXIT',{badarg,_}} = (catch qlc:e(H)),
          {'EXIT',{badarg,_}} = (catch qlc:cursor(H)),
          F = fun(Obj, A) -> A++[Obj] end,
          {'EXIT',{badarg,_}} = (catch qlc:fold(F, [], H))">>,

       <<"Q1 = qlc:q([X || X <- [1,2]]),
          AL = [Q1, [1,2,3]],
          Q2 = qlc:q([X || X <- qlc:sort(qlc:append(AL))]),
          [1,1,2,2,3] = qlc:e(Q2)">>,

       <<"H = qlc:q([{X,Y} || X <- qlc:sort(qlc:q([X || X <- [1,2,3,2]]), 
                                            {unique,true}),
                              Y <- [a,b]]),
          {qlc, _,
           [{generate, _, {sort, {qlc, _, [{generate, _, {list, [1,2,3,2]}}],
                                  [{unique,true}]},
                           []}},
            {generate, _, {qlc, _, [{generate, _, {list, [a,b]}}],
                           [{unique,true}]}}],
           [{unique,true}]} = i(H, unique_all),
          [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H, unique_all)">>,

       <<"L = [1,2,1,3,4,3,1],
          true = lists:sort(L) == qlc:e(qlc:q([X || X <- qlc:sort(L)])),
          true = lists:usort(L) == 
                 qlc:e(qlc:q([X || X <- qlc:sort(L, {unique,true})])),
          true = lists:reverse(lists:sort(L)) ==
                 qlc:e(qlc:q([X || X <- qlc:sort(L, {order, descending})])),
          true = lists:reverse(lists:usort(L)) ==
                 qlc:e(qlc:q([X || X <- qlc:sort(L, [{order, descending}, 
                                                     {unique, true}])])),
          CF = fun(X, Y) -> X =< Y end,
          true = lists:sort(L) == 
                 qlc:e(qlc:q([X || X <- qlc:sort(L, {order, CF})])),
          true = lists:usort(L) ==
                 qlc:e(qlc:q([X || X <- qlc:sort(L, [{order, CF}, 
                                                    {unique, true}])]))">>,

       <<"E = ets:new(foo, []),
          [true || I <- lists:seq(1, 50000), not ets:insert(E, {I, I})],
          H = qlc:q([{X,Y} || X <- [a,b], Y <- qlc:sort(ets:table(E))]),
          100000 = length(qlc:e(H)),
          ets:delete(E)">>

        ],
    run(Config, Ts),
    ok.

%% The sort option.
keysort(Config) when is_list(Config) ->

    Ts = [
       <<"OF = fun(X, Y) -> X =< Y end,
          F = fun(Obj, A) -> A++[Obj] end,
          H = qlc:q([X || X <- qlc:keysort(1, [{2,a},{1,b}], {order,OF})]),
          {'EXIT',{{badarg,order},_}} = (catch qlc:e(H)),
          {'EXIT',{{badarg,order},_}} = (catch qlc:fold(F, [], H)),
          {'EXIT',{{badarg,order},_}} = (catch qlc:cursor(H))">>,

       <<"E = create_ets(1, 2),
          H = qlc:q([X || X <- qlc:keysort([1], ets:table(E), 
                                           [{size,1},{tmpdir,\"/a/b/c\"}])]),
          H1 = qlc:q([X || {X,_} <- qlc:e(H), X < 4]),
          {error,_,{file_error,_,_}} = qlc:info(H1),
          {error,_,{file_error,_,_}} = qlc:e(H1),
          ets:delete(E)">>,

       <<"L = [{1,a},{2,b},{3,c},{2,b}],
          H = qlc:q([{X,Y} || {X,_} <- qlc:keysort(1, qlc:q([X || X <- L]), 
                                                   {unique,true}),
                              Y <- [a,b]]),
          [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = qlc:e(H),
          C = qlc:cursor(H),
          [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] = 
              qlc:next_answers(C, all_remaining),
          qlc:delete_cursor(C)">>,

       <<"H1 = qlc:q([X || X <- qlc:keysort(0, [])]),
          {'EXIT',{badarg,_}} = (catch qlc:e(H1)),
          H2 = qlc:q([X || X <- qlc:keysort(1, [], {bad,arg})]),
          {'EXIT',{badarg,_}} = (catch qlc:e(H2)),
          H3 = qlc:q([X || X <- qlc:keysort([], [])]),
          {'EXIT',{badarg,_}} = (catch qlc:e(H3))">>,

       <<"H = qlc:q([X || X <- qlc:keysort(1, [{1,a},{2,b}], 
                                           [{order,descending},
                                           {compressed,true}])]),
          [{2,b},{1,a}] = qlc:e(H),
          H2 = qlc:q([X || X <- qlc:keysort(1, [{1},{2}], compressed)]),
          {'EXIT', {badarg, _}} = (catch qlc:e(H2))">>,

       <<"H = qlc:q([X || X <- qlc:keysort(1, [{1,a},{2,b}], {compressed,false})]),
          [{1,a},{2,b}] = qlc:e(H)">>,

       <<"E = create_ets(1, 2),
          H = qlc:q([X || X <- qlc:keysort([1], ets:table(E), 
                                           [{size,1},{tmpdir,\"/a/b/c\"}])]),
          F = fun(Obj, A) -> A++[Obj] end,
          {error,_,{file_error,_,_}} = qlc:e(H),
          \" \\\"no such\" ++ _ = lists:dropwhile(fun(A) -> A =/= $\s end, 
                                 lists:flatten(qlc:format_error(qlc:e(H)))),
          {error,_,{file_error,_,_}} = qlc:e(H, {unique_all,true}),
          {error,_,{file_error,_,_}} = qlc:cursor(H),
          {error,_,{file_error,_,_}} = qlc:cursor(H, {unique_all,true}),
          {error,_,{file_error,_,_}} = qlc:cursor(H, {spawn_options, []}),
          {error,_,{file_error,_,_}} = qlc:cursor(H, {spawn_options,default}),
          {error,_,{file_error,_,_}} = 
               qlc:cursor(H, [{unique_all,true},{spawn_options, []}]),
          {error,_,{file_error,_,_}} = qlc:fold(F, [], H),
          {error,_,{file_error,_,_}} = qlc:fold(F, [],H, {unique_all,true}),
          ets:delete(E)">>,

       <<"L = [{1,b,a},{1,b,b},{1,a,a}],
          H = qlc:q([X || X <- qlc:keysort([4,1], L)]),
          {error,_,bad_object} = qlc:e(H),
          \"the keys\" ++ _ = qlc:format_error(qlc:e(H))">>,

       begin
       File = filename:join(?privdir, "afile"),
       ok = file:write_file(File, <<>>),
       [<<"H = qlc:q([X || X <- qlc:keysort([1], [{1},{2},{1}],
                                            [{tmpdir,\"">>, File, <<"\"},
                                             {size,1}])]),
           {error,_,{file_error,_,_}} = qlc:e(H),
           file:delete(\"">>, File, <<"\")">>]
       end,

       <<"H0 = qlc:q([X || X <- [1,2,3]]),
          H = qlc:q([X || X <- qlc:sort(H0,{tmpdir,\".\"})]),
          [1,2,3] = qlc:e(H)">>,

       %% The global option 'tmpdir' takes precedence.
       begin
       PrivDir = ?privdir,
       [<<"L = [{1,a},{2,b}],
           H = qlc:q([X || X <- qlc:keysort([1], L, {tmpdir,\"/a/b/c\"})]),
           H1 = qlc:q([X || X <- H, X > 3]),
           Dir = \"">>, PrivDir, <<"\",
           Options = [{tmpdir, Dir}],
           {qlc,_,[{generate,_,{keysort,{list,L},[1],[{tmpdir,Dir}]}},_],[]} =
                i(H1, Options),
           [{1,a},{2,b}] = qlc:e(H1, Options)">>] % no check of "/a/b/c"
       end,

       <<"L = [{2,c},{1,b},{1,a},{3,a},{1,c},{2,b}],
          true = lists:sort(L) == 
                 qlc:e(qlc:q([X || X <- qlc:keysort([1,2], L)]))">>,

       <<"L = [{1,b},{2,c},{1,a},{3,e},{4,f},{3,d}],
          true = lists:keysort(1, L) ==
                 qlc:e(qlc:q([X || X <- qlc:keysort(1,L)])),
          true = lists:ukeysort(1, L) ==
                 qlc:e(qlc:q([X || X <- qlc:keysort(1, L, {unique,true})])),
          true = lists:reverse(lists:keysort(1, L)) ==
                 qlc:e(qlc:q([X || X <- qlc:keysort(1,L, 
                                                    {order, descending})])),
          true = lists:reverse(lists:ukeysort(1, L)) ==
                 qlc:e(qlc:q([X || X <- qlc:keysort(1, L, [{unique,true},
                                                {order, descending}])]))">>,

       <<"L = [{X} || X <- lists:seq(1,100000)],
          H1 = qlc:append([L,[{1,2},{2,3},{3,4}]]),
          H = qlc:keysort([1], qlc:keysort([1], H1, [{compressed,true}])),
          R = qlc:e(H),
          100003 = length(R)">>

        ],
    run(Config, Ts),

    ok.

%% keysort/1,2, using a file.
filesort(Config) when is_list(Config) ->
    Ts = [
       <<"Q = qlc:q([X || X <- [{3},{1},{2}]]),
          Opts = [{size,10},{no_files,3}],
          Q2 = qlc:q([{X,Y} || Y <- [1,2], X <- qlc:keysort([1],Q,Opts)]),
          [{{1},1},{{2},1},{{3},1},{{1},2},{{2},2},{{3},2}] = qlc:e(Q2)">>
        ],
    run(Config, Ts),
    ok.


%% The cache option.
cache(Config) when is_list(Config) ->
    Ts = [
       <<"{'EXIT', {badarg, _}} = (catch qlc:q([X || X <- [1,2]], badarg))">>,

       <<"Q1 = qlc:q([X || X <- [1,2,1,2,1]], {unique,true}),
          [1,2] = qlc:e(Q1),
          [1,2] = qlc:e(Q1, {unique_all,true}),
          Q2 = qlc:q([X || X <- qlc:q([X || X <- [1,2,1,2,1]],
                                      {unique,true})]),
          [1,2] = qlc:e(Q2),
          [1,2] = qlc:e(Q2, {unique_all,true}),
          Q3 = qlc:q([X || X <- qlc:append([[1,2,3], [4,5,6]])]),
          [1,2,3,4,5,6] = qlc:e(Q3)">>,

       <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{2,a},{1,b},{2,b}]]),
          Q2 = qlc:q([{X,make_ref()} || X <- Q1]),
          [{1,_},{2,_},{1,_},{2,_}] = qlc:e(Q2, {unique_all,false}),
          [{1,_},{2,_}] = qlc:e(Q2, {unique_all,true})">>,

       <<"E = ets:new(test, [duplicate_bag]),
          true = ets:insert(E, [{1,a},{2,a},{1,b},{2,b}]),
          Q1 = qlc:q([X || {X,_} <- ets:table(E)]),
          Q2 = qlc:q([{X,make_ref()} || X <- Q1]),
          [{1,_},{1,_},{2,_},{2,_}] = lists:sort(qlc:e(Q2, {unique_all,false})),
          [{1,_},{2,_}] = lists:sort(qlc:e(Q2, {unique_all,true})),
          ets:delete(E)">>,

       <<"Q1 = qlc:q([X || {X,_} <- [{1,a},{2,a},{1,b},{2,b}]]),
          Q2 = qlc:q([{X,make_ref()} || X <- qlc:append([Q1, Q1])]),
          [{1,_},{2,_},{1,_},{2,_},{1,_},{2,_},{1,_},{2,_}] = 
                qlc:e(Q2, {unique_all,false}),
          [{1,_},{2,_}] = qlc:e(Q2, {unique_all,true})">>,

       <<"[] = qlc:e(qlc:q([X || X <- []], {unique, true})),
          [] = qlc:e(qlc:q([X || X <- qlc:q([X || X <- qlc:append([])], 
                                             {unique,true})]))">>,

       <<"Q1 = qlc:q([{X,make_ref()} || {X,_} <- [{1,a},{1,b}]]),
          [{1,_},{1,_}] = qlc:e(Q1, {unique_all, true}),
          Q2 = qlc:q([Y || {X,_} <- [{1,a},{1,b}],
                           begin Y = {X,make_ref()}, true end]),
          [{1,_},{1,_}] = qlc:e(Q2, {unique_all,true}),
          Q3 = qlc:q([Y || X <- [{1,a},{2,a}], 
                           begin {_,Z} = X, Y = {Z,make_ref()}, true end]),
          [{a,_},{a,_}] = qlc:e(Q3, {unique_all, true})">>,

       <<"E = ets:new(apa, [duplicate_bag]),
          ets:insert(E, [{1,a},{2,a},{1,a}]),
          H1 = qlc:q([X || X <- qlc:append(ets:table(E),[7,3,5])], 
                          {cache, true}),
          [{_,a},{_,a},{_,a},7,3,5] = qlc:e(H1),
          ets:delete(E)">>,
          
       <<"F = fun(Obj, A) -> A++[Obj] end,
          H = qlc:q([X || X <- [1,3,2,4]], cache),
          Q = qlc:q([X || X <- H]),
          [1,3,2,4] = qlc:fold(F, [], Q, [])">>,

       <<"F = fun(Obj, A) -> A++[Obj] end,
          E = ets:new(apa, [duplicate_bag]),
          true = ets:insert(E, [{1,a},{2,b},{1,a}]),
          Q1 = qlc:q([X || X <- ets:table(E)], [cache, unique]),
          Q = qlc:q([X || X <- Q1], [cache, unique]),
          {qlc, _, [{generate, _,{table,_}}], [{unique,true}]} = i(Q),
          R = qlc:fold(F, [], Q, []),
          ets:delete(E),
          true = [{1,a},{2,b}] == lists:sort(R)">>,

       <<"E = ets:new(apa, [duplicate_bag]),
          ets:insert(E, [{1,a},{2,b},{1,a}]),
          H1 = qlc:q([X || X <- qlc:append(ets:table(E),[7,3])], cache),
          H2 = qlc:q([{Y,X} || Y <- [2,1,3], X <- H1]),
          [{2,_},{2,_},{2,_},{2,7},{2,3},
           {1,_},{1,_},{1,_},{1,7},{1,3},
           {3,_},{3,_},{3,_},{3,7},{3,3}] = qlc:e(H2),
          ets:delete(E)">>,

          %% This case is not 100 percent determined. An Ets table
          %% is updated in a filter and later used in a generator.
       <<"E = ets:new(apa, [bag]),
          true = ets:insert(E, [{1,a},{2,b}]),
          H1 = qlc:q([Y || Y <- ets:table(E)], 
                     [{cache, no}, {unique, true}]),
          H = qlc:q([{X,Y} || X <- [{1,a},{2,d},{3,e}],
                              ets:insert(E, X),
                              Y <- H1]),
          [{{1,a},_}, {{1,a},_}, {{2,d},_}, {{2,d},_}, {{2,d},_}, 
           {{3,e},_}, {{3,e},_}, {{3,e},_}, {{3,e},_}] = qlc:e(H),
          ets:delete(E)">>,

          %% This case is not 100 percent determined. An Ets table
          %% is updated in a filter and later used in a generator.
       <<"E = ets:new(apa, [bag]),
          true = ets:insert(E, [{1,a},{2,b}]),
          H1 = qlc:q([Y || Y <- ets:table(E)], 
                     [{cache, true}, {unique, true}]),
          H = qlc:q([{X,Y} || X <- [{1,a},{2,d},{3,e}],
                              ets:insert(E, X),
                              Y <- H1]),
          [{{1,a},_}, {{1,a},_}, {{2,d},_}, {{2,d},_}, {{3,e},_}, {{3,e},_}] = 
              qlc:e(H),
          ets:delete(E)">>,

       <<"%% {5979} and {5549} are both hashed to 28244 by phash2/1
          E = ets:new(apa, [duplicate_bag]),
          true = ets:insert(E, [{5979},{5549},{5979},{5549},{0}]),
          H1 = qlc:q([X || X <- ets:table(E)], 
                     [{cache, true}, {unique, true}]),
          H = qlc:q([Y || _ <- [1,2], Y <- H1]),
          {qlc, _, [{generate, _, {list, [1,2]}},
                    {generate, _, {qlc, _, [{generate, _, {table,_}}],
                                   [{cache,ets},{unique,true}]}}],
           []} = i(H),
          [{0},{0},{5549},{5549},{5979},{5979}] = lists:sort(qlc:e(H)),
          ets:delete(E)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H1 = qlc:q([X || X <- ets:table(E)], [cache, unique]),
          H2 = qlc:q([X || Y <- [3,4], ets:insert(E, {Y}), X <- H1]),
          {qlc, _, [{generate, _, {list, [3,4]}}, _, 
                    {generate, _, {qlc, _, [{generate, _, 
                                             {table, _}}], 
                                   [{cache, ets}]}}], 
           []} = i(H2),
          [{1},{2},{3},{1},{2},{3}] = qlc:e(H2),
          ets:delete(E)">>,

       <<"E = ets:new(apa, [ordered_set]),
          ets:insert(E, [{1},{2}]),
          H1 = qlc:q([X || X <- ets:table(E)], [unique]),
          H2 = qlc:q([X || Y <- [3,4], ets:insert(E, {Y}), X <- H1]),
          [{1},{2},{3},{1},{2},{3},{4}] = qlc:e(H2),
          ets:delete(E)">>,

       <<"H0 = qlc:append([a,b], [c,d]),
          H = qlc:q([{X,Y} || 
                        X <- H0,
                        Y <- qlc:q([{X1,Y} || 
                                       X1 <- H0,
                                       Y <- qlc:q([{X2,Y} || 
                                                      X2 <- H0,
                                                      Y <- H0])])]),
          {qlc, _,
           [{generate, _,{append, [{list, [a,b]}, {list, [c,d]}]}},
            {generate, _, 
             {qlc, _,
              [{generate, _, {append,[{list, [a,b]},{list, [c,d]}]}},
               {generate, _, 
                {qlc, _,
                 [{generate, _,{append,[{list, [a,b]}, {list, [c,d]}]}},
                  {generate, _,{append,[{list, [a,b]}, {list, [c,d]}]}}],
                 [{cache,ets}]}}],
              [{cache,ets}]}}],
           []} = i(H, cache_all)">>
       
       ],
    run(Config, Ts),
    ok.

%% OTP-6038. The {cache,list} option.
cache_list(Config) when is_list(Config) ->
    Ts = [
       begin
       PrivDir = ?privdir,
       [<<"%% unique, cache list. A simple qlc.
          Options = [{cache,list}, unique],
          L0 = [1,2,3,4,1,2,3,4],
          L = qlc_SUITE:table(L0, []),
          Q1 = qlc:q([X || X <- L], Options),
          Q = qlc:q([{X,Y} || X <- [a,b], Y <- Q1]),
          GOptions = [{tmpdir,\"">>, PrivDir, <<"\"}],
          {qlc,_,[{generate,_,{list,[a,b]}},
                  {generate,_,{qlc,_,
                               [{generate,_,{table,_}}],
                               [{cache,list},{unique,true}]}}],
           []} = i(Q, GOptions),
          true = [{X,Y} || X <- [a,b], Y <- [1,2,3,4]] =:= 
                 qlc:e(Q, GOptions)">>]
       end,

       begin
       MS = ets:fun2ms(fun({X,_}) when X > 1 -> X end),
       [<<"%% No cache, even if explicit match specification.
          etsc(fun(E) ->
                   MS =  ">>, io_lib:format("~w", [MS]), <<",
                   Options = [{cache,list}, unique],
                   Q = qlc:q([{X,Y} || 
                                 X <- ets:table(E, {traverse, {select, MS}}),
                                 Y <- [1,2,3]], 
                             Options),
                   {qlc,_,[{generate,_,{table,{ets,table,_}}},
                           {generate,_,{list,[1,2,3]}}],
                    [{unique,true}]} = i(Q),
                   true = [{X,Y} || X <- lists:seq(2,10), Y <- [1,2,3]] =:=
                       lists:sort(qlc:e(Q))
               end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>]
       end,

       <<"%% Temporary files.
          %% Remove list expression caches when possible. (no visible effect)
          T = lists:seq(1, 100000), % Huge terms on file
          F = fun(C) ->
                      Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 
                                 {cache,C}),
                      Q1 = qlc:q([{X,Y,Z} ||
                                     X <- Q0,
                                     Y <- Q0,
                                     Z <- Q0],
                                 {cache,C}),
                      qlc:q([{X, Y} || Y <- [1], X <- Q1])
              end,
          Ql = F(list),
          Rl = qlc:e(Ql, {max_list_size, 64*1024}),
          Qe = F(ets),
          Re = qlc:e(Qe),
          Qf = F(no),
          Rf = qlc:e(Qf),
          Ri = qlc:e(Ql, {max_list_size, 1 bsl 35}), % heavy
          {27,27,27,27,true,true,true} = 
              {length(Rl), length(Re), length(Rf), length(Ri),
               Rl =:= Re, Re =:= Rf, Rf =:= Ri}">>,

       <<"%% File sorter.
          T = lists:seq(1, 10000),
          F = fun(C) ->
                      Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 
                                 [{cache,C},unique]),
                      Q1 = qlc:q([{X,Y,Z} ||
                                     X <- Q0,
                                     Y <- Q0,
                                     Z <- Q0],
                                 [{cache,C},unique]),
                      qlc:q([{X, Y} || Y <- [1], X <- Q1])
              end,
          GOpt = [{max_list_size, 10000}],
          Ql = F(list),
          Rl = qlc:e(Ql, GOpt),
          Qe = F(ets),
          Re = qlc:e(Qe, GOpt),
          Qf = F(no),
          Rf = qlc:e(Qf, GOpt),
          {1,1,1,true,true} = 
              {length(Rl), length(Re), length(Rf), Rl =:= Re, Re =:= Rf}">>,

       <<"%% Remove list expression caches when possible. (no visible effect)
          Q0 = qlc:q([{X} || X <- [1,2,3], begin X > 0 end], {cache,list}),
          Q1 = qlc:q([{X,Y,Z} ||
                         X <- Q0,
                         Y <- Q0,
                         Z <- Q0],
                     {cache,list}),
          Q = qlc:q([{X, Y} || Y <- [1], X <- Q1]),
          R = qlc:e(Q),
          L0 = [{X} || X <- [1,2,3], begin X > 0 end],
          L1 = [{X,Y,Z} ||
                   X <- L0,
                   Y <- L0,
                   Z <- L0],
          L = [{X, Y} || Y <- [1], X <- L1],
          true = R =:= L">>,

       <<"%% No temporary file.
          L = [{I,a} || I <- lists:seq(1, 10)],
          Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err),
                           begin element(1, X) > 5 end],
                     {cache,list}),
          Q = qlc:q([{X, element(1,Y)} || 
                        X <- lists:seq(1, 5),
                        Y <- Q0]),
          err = qlc:e(Q)">>,

       <<"%% Sort internally.
          L = [{I,a} || I <- lists:seq(1, 10)],
          Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err),
                           begin element(1, X) > 5 end],
                     [unique,{cache,list}]),
          Q = qlc:q([{X, element(1,Y)} || 
                        X <- lists:seq(1, 5),
                        Y <- Q0]),
          err = qlc:e(Q, {max_list_size,0})">>,

       <<"%% No temporary file.
          etsc(fun(E) ->
                       Q0 = qlc:q([X || X <- ets:table(E),
                                        begin element(1, X) > 5 end],
                                  {cache,list}),
                       Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5),
                                                       Y <- Q0]),
                       R = [{X,Y} || X <- lists:seq(1, 5), 
                                     Y <- lists:seq(6, 10)],
                       R = lists:sort(qlc:e(Q))
               end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>,

       <<"%% Sort internally
          etsc(fun(E) ->
                       Q0 = qlc:q([X || X <- ets:table(E),
                                        begin element(1, X) > 5 end],
                                  [{cache,list},unique]),
                       Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5),
                                                       Y <- Q0]),
                       R = [{X,Y} || X <- lists:seq(1, 5), 
                                     Y <- lists:seq(6, 10)],
                       R = lists:sort(qlc:e(Q))
               end, [{keypos,1}], [{I,a} || I <- lists:seq(1, 10)])">>,

       <<"%% A few more tests of unique and {cache,list}.
          F = fun(CU) ->
                      H1 = qlc:q([{X,Y} || 
                                     Y <- [a,b], 
                                     X <- [1,2]],
                                 CU),
                      qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1])
              end,
          Q1 = F([]),
          Q2 = F([{cache,list}, unique]),
          R1 = qlc:e(Q1),
          R2 = qlc:e(Q2),
          R3 = qlc:e(Q2, {max_list_size, 0}), % still in RAM
          true = R1 =:= R2,
          true = R2 =:= R3">>,

       <<"E = ets:new(t, [duplicate_bag]),
          true = ets:insert(E, [{2},{1},{2}]),
          H1 = qlc:q([{X,Y} || 
                         Y <- [a,b], 
                         {X} <- ets:table(E)], 
                     [{cache,list}, unique]),
          H2 = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]),
          {qlc,_,[{generate,_,{list,[3,4]}},
                  {generate,_,{qlc,_,
                               [{generate,_,{list,[a,b]}},
                                {generate,_,
                                 {qlc,_,[{generate,_,{table,{ets,table,_}}}],
                                  [{cache,list},{unique,true}]}}],
                               [{cache,list},{unique,true}]}}], []} = i(H2),
          L1s = [[{X,Y} || Y <- [a,b], X <- Xs] || Xs <- [[1,2], [2,1]]],
          L2s = [[{X,Y,Z} || X <- [3,4], {Y,Z} <- L1] || L1 <- L1s],
          R1 = qlc:e(H2),
          R2 = qlc:e(H2, {max_list_size, 0}), % on temporary file
          ets:delete(E),
          true = lists:member(R1, L2s),
          true = R1 =:= R2">>,

       <<"E = ets:new(t, [duplicate_bag]),
          true = ets:insert(E, [{2},{1},{2}]),
          H1 = qlc:q([{X,Y} || 
                         Y <- [a,b], 
                         {X} <- ets:table(E)], 
                     [{cache,list}]),
          H2 = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1]),
          L1s = [[{X,Y} || Y <- [a,b], X <- Xs] || Xs <- [[1,2,2], [2,2,1]]],
          L2s = [[{X,Y,Z} || X <- [3,4], {Y,Z} <- L1] || L1 <- L1s],
          R1 = qlc:e(H2),
          R2 = qlc:e(H2, {max_list_size, 0}), % on temporary file
          ets:delete(E),
          true = lists:member(R1, L2s),
          true = R1 =:= R2">>,

       <<"Q1 = qlc:q([{X,Y} || 
                         Y <- [a,b], 
                         {X,_} <- qlc_SUITE:table_error([{a,1}], 2, err)],
                     [{cache,list}, unique]),
          Q = qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- Q1]),
          {qlc,_,[{generate,_,{list,[3,4]}},
                  {generate,_,{qlc,_,
                               [{generate,_,{list,[a,b]}},
                                {generate,_,{table,_}}],
                               [{cache,list},{unique,true}]}}],
           []} = i(Q),
          err = qlc:e(Q,{max_list_size,0})">>,

       begin
       Privdir = ?privdir,
       [<<"
          E = ets:new(t, [duplicate_bag]),
          N = 17000,
          true = ets:insert(E, [{X,X} || X <- lists:seq(1, N)]),
          N = ets:info(E, size),
          RF = fun(GOpts) ->
                       F = fun(CU) ->
                                   H1 = qlc:q([{X,Y} || 
                                                  Y <- [a,b], 
                                                  {X,_} <- ets:table(E)], 
                                              CU),
                                   qlc:q([{X,Y,Z} || X <- [3,4], {Y,Z} <- H1])
                           end,
                       Q1 = F([{cache,list}, unique]),
                       _ = qlc:info(Q1, GOpts),
                       R1 = qlc:e(Q1, GOpts),
                       Q2 = F([unique]),
                       R2 = qlc:e(Q2, GOpts),
                       true = lists:sort(R1) =:= lists:sort(R2)
               end,
          GOpts = [{tmpdir,\"">>,Privdir,<<"\"}],
          RF([{max_list_size, 1 bsl 35} | GOpts]),
          RF(GOpts),
          RF([{max_list_size, 40000} | GOpts]),
          true = ets:insert(E, [{X,X} || X <- lists:seq(1, N)]),
          true = N+N =:= ets:info(E, size),
          RF([{max_list_size, 1 bsl 30} | GOpts]),
          RF(GOpts),
          RF([{max_list_size, 40000} | GOpts]),
          ets:delete(E)">>]
       end,

       <<"%% Temporary file employed.
          etsc(fun(E) ->
                       Q0 = qlc:q([X || X <- ets:table(E),
                                        begin element(1, X) > 5 end],
                                  {cache,list}),
                       Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5),
                                                       Y <- Q0]),
                       R = [{X,Y} || X <- lists:seq(1, 5), 
                                     Y <- lists:seq(6, 10)],
                       R = lists:sort(qlc:e(Q, {max_list_size, 100*1024}))
               end, [{keypos,1}], [{I,a,lists:duplicate(100000,1)} || 
                                        I <- lists:seq(1, 10)])">>,

       <<"%% Temporary file employed. The file is removed after error.
          L = [{I,a,lists:duplicate(100000,1)} || I <- lists:seq(1, 10)],
          Q0 = qlc:q([X || X <- qlc_SUITE:table_error(L, 1, err),
                           begin element(1, X) > 5 end],
                     {cache,list}),
          Q = qlc:q([{X, element(1,Y)} || 
                        X <- lists:seq(1, 5),
                        Y <- Q0]),
          err = qlc:e(Q)">>,

       <<"%% Temporary file employed. The file is removed after error.
          L = [{I,a,lists:duplicate(100000,1)} || I <- lists:seq(1, 10)],
          Q0 = qlc:q([X || X <- qlc_SUITE:table(L, 1, []),
                           begin element(1, X) > 5 end],
                     {cache,list}),
          Q = qlc:q([{X, element(1,Y)} || 
                        X <- lists:seq(1, 5),
                        Y <- Q0]),
          {error, _, {file_error,_,_}} = qlc:e(Q, {tmpdir, \"/a/b/c\"})">>,

       <<"Q = qlc:q([X || X <- [1,2]]),
          {'EXIT', {badarg, _}} = (catch qlc:e(Q, {max_list_size, -1}))">>,

       <<"Q = qlc:q([X || X <- [1,2]]),
          {'EXIT', {badarg, _}} = (catch qlc:e(Q, {max_list_size, foo}))">>

       ],
    run(Config, Ts),
    ok.

%% Filters and match specs.
filter(Config) when is_list(Config) ->
    Ts = [
       <<"L = [1,2,3,4,5],
          QH1 = qlc:q([X || X <- L, X > 1, X < 4]),
          [2,3] = qlc:e(QH1),
          {list,{list,L},_MS} = i(QH1)
         ">>,

       <<"L = [1,2,3,4,5],
          QH2 = qlc:q([X || X <- L, X > 1, X < 4, X > 2]),
          [3] = qlc:e(QH2),
          {list,{list,L},_MS} = i(QH2)
         ">>,

          %% "X > 1" is skipped since the matchspec does the job
       <<"QH3 = qlc:q([X || X <- [1,2,3,4,5], X > 1, begin X < 4 end]),
          [2,3] = qlc:e(QH3),
          {qlc,_,[{generate,_,{list,{list,[1,2,3,4,5]},_MS}},_],[]} = i(QH3)
         ">>,

       <<"QH4 = qlc:q([{X,Y} || X <- [1,2], Y <- [1,2]]),
          [{1,1},{1,2},{2,1},{2,2}] = qlc:e(QH4),
          {qlc,_,[{generate,_,{list,[1,2]}},{generate,_,{list,[1,2]}}],[]} =
              i(QH4)">>,

          %% "X > 1" is skipped since the matchspec does the job
       <<"QH5 = qlc:q([{X,Y} || X <- [1,2], X > 1, Y <- [1,2]]),
          [{2,1},{2,2}] = qlc:e(QH5),
          {qlc,_,[{generate,_,{list,{list,[1,2]},_MS}},
                  {generate,_,{list,[1,2]}}],[]} = 
              i(QH5)">>,

       <<"%% Binaries are not handled at all when trying to find lookup values
          etsc(fun(E) ->
                      A = 2, 
                      Q = qlc:q([X || {X} <- ets:table(E), <<A>> =:= <<X>>]),
                      [2] = lists:sort(qlc:e(Q)),
                      false = lookup_keys(Q)
              end, [{1},{2},{3}])">>,

       <<"etsc(fun(E) ->
                      Q = qlc:q([X || {X,_} <- ets:table(E), 
                                   qlc:e(qlc:q([Y || {Y,_} <- ets:table(E), 
                                                              Y > X])) == []]),
                      [3] = qlc:e(Q)
              end, [{1,a},{2,b},{3,c}])">>,

       <<"Q = qlc:q([X || {X} <- [], (false or (X/0 > 3))]),
          \"[]\" = qlc:info(Q),
          [] = qlc:e(Q)">>,

       <<"%% match spec
          [] = qlc:e(qlc:q([X || {X} <- [{1},{2}], 
                                 (false orelse (X/0 > 3))])),
          %% generated code
          {'EXIT', {badarith, _}} = 
            (catch qlc:e(qlc:q([X || {X} <- [{1}], 
                                     begin (false orelse (X/0 > 3)) end])))">>,

       <<"%% Partial evaluation in filter.
          etsc(fun(E) ->
                     QH = qlc:q([{X+1,Y} || {X,Y} <- ets:table(E), 
                                            X =:= 1-1+1+(+1)]),
                     [{3,2}] = qlc:e(QH),
                     [2] = lookup_keys(QH)
              end, [{1,1},{2,2},{3,3}])">>,

       <<"%% =/2 in filters must not be recognized when 'badmatch' is
          %% possible.
          etsc(fun(E) ->
                     QH = qlc:q([{X,Y} || {X,Y} <- ets:table(E),
                                          ((Y = X) =:= 3)]),
                     {'EXIT', {{badmatch,4},_}} = (catch qlc:e(QH)),
                     false = lookup_keys(QH)
               end, [{3,3},{4,true}])">>,

       <<"%% One more of the same kind.
          etsc(fun(E) ->
                      QH = qlc:q([{X,Y} || {X,_} <- ets:table(E), 
                                           (Y=X) =:= (Y=1+1)]),
                      {'EXIT', {{badmatch,2},_}} = (catch qlc:e(QH)),
                      false = lookup_keys(QH)
              end, [{1,1},{2,2},{3,3}])">>,

       <<"%% OTP-5195. Used to return a value, but badarith is correct.
          etsc(fun(E) ->
                      QH = qlc:q([X || {X,_} <- ets:table(E), 
                                       (X =:= 1) and
                                    if X =:= 1 -> true;
                                       true -> X/0
                                    end]),
                      {'EXIT',{badarith,_}} = (catch qlc:e(QH)),
                      false = lookup_keys(QH)
              end, [{1,1},{2,2},{3,3}])">>,

       <<"fun(Z) ->
            Q = qlc:q([X || Z < 2, X <- [1,2,3]]),
            [] = qlc:e(Q)
          end(3)">>,

       <<"H = qlc:q([{P1,A,P2,B,P3,C} ||
                  P1={A,_} <- [{1,a},{2,b}],
                  {_,B}=P2 <- [{1,a},{2,b}],
                  C=P3 <- [1],
                  {[X,_],{_,X}} <- [{[1,2],{3,1}}, {[a,b],{3,4}}],
                  A > 0,
                  B =/= c,
                  C > 0]),
          L = [{{1,a},1,{1,a},a,1,1}, {{1,a},1,{2,b},b,1,1},
               {{2,b},2,{1,a},a,1,1}, {{2,b},2,{2,b},b,1,1}],
          L = qlc:e(H)">>,

       <<"H = qlc:q([{X,Y} ||
                  X = _ <- [1,2,3],
                  _ = Y <- [a,b,c],
                  _ = _ <- [foo],
                  X > 1,
                  Y =/= a]),
          [{2,b},{2,c},{3,b},{3,c}] = qlc:e(H)">>

       ],
    run(Config, Ts),
    ok.

%% info/2.
info(Config) when is_list(Config) ->
    Ts = [
       <<"{list, [1,2]} = i(qlc:q([X || X <- [1,2]])),
          {append,[{list, [1,2]}, {list, [3,4]}]} = 
               i(qlc:append([1,2],[3,4])),
          {sort,{list, [1,2]},[]} = i(qlc:sort([1,2])),
          E = ets:new(foo, []),
          ets:insert(E, [{1},{2}]),
          {table, _} = i(ets:table(E)),
          true = ets:delete(E),
          {list, [1,2]} = i([1,2]),
          {append, [{list, [1,2]}, {list, [3,4]}]} =
             i(qlc:q([X || X <- qlc:append([1,2],[3,4])])),
          
          H0 = qlc:q([X || X <- throw({throw,t})]),
          {throw,t} = (catch {any_term,qlc:info(H0)}),
          {'EXIT', {badarg, _}} = 
               (catch qlc:info(foobar)),
          {'EXIT', {badarg, _}} = 
               (catch qlc:info(qlc:q([X || X <- [1,2]]), badarg))">>,

       <<"{'EXIT', {badarg, _}} = 
               (catch qlc:info([X || {X} <- []], {n_elements, 0})),
          L = lists:seq(1, 1000),
          \"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 | '...']\" = qlc:info(L, {n_elements, 10}),
          {cons,A1,{integer,A2,1},{atom,A3,'...'}} =
            qlc:info(L, [{n_elements, 1},{format,abstract_code}]),
          1 = erl_anno:line(A1),
          1 = erl_anno:line(A2),
          1 = erl_anno:line(A3),
          Q = qlc:q([{X} || X <- [a,b,c,d,e,f]]),
          {call,_,_,[{cons,_,{atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c},
                                                            {atom,_,'...'}}}},
                     {call,_,_,_}]} = 
          qlc:info(Q, [{n_elements, 3},{format,abstract_code}]),
          \"ets:match_spec_run([a, b, c, d, e, f],\n\"
          \"                   ets:match_spec_compile([{'$1', [true], \"
          \"[{{'$1'}}]}]))\" = 
             qlc:info(Q, [{n_elements, infinity}])">>,

       <<"Q1 = qlc:q([{X} || X <- qlc:q([X || X <- [1,2]])]),
          {qlc, _, [{generate, _, {list, [1,2]}}],[]} = i(Q1),
          Q2 = qlc:q([X || X <- qlc:q([{X} || X <- [1,2]])]),
          {list,{list,[1,2]},_} = i(Q2),
          [{1},{2}] = qlc:eval(Q2),
          Q3 = qlc:q([{X,Y} || X <- qlc:q([X || X <- [a,b]]),       
                               Y <- qlc:q([Z || Z <- [a,b]])]),
          {qlc, _, [{generate, _, {list, [a,b]}}, 
                    {generate, _, {list, [a,b]}}], []} = i(Q3),
          Q4 = qlc:q([X || X <- [a]]),
          {list, [a]} = i(Q4),
          Q5 = qlc:q([X || X <- qlc:q([Y || Y <- [a,b]], unique)]),
          {qlc, _, [{generate, _, {list, [a,b]}}], [{unique,true}]} = 
             i(Q5)">>,

       <<"H = qlc:q([X || X <- qlc:append([qlc:q([X || X <- [1,2]]),[1,2]])]),
          {append, [{list, [1,2]},{list, [1,2]}]} = i(H),
          [1,2,1,2] = qlc:e(H)">>,

       <<"H = qlc:q([{X} || X <- [], X > 1]),
          {list, []} = i(H),
          [] = qlc:e(H)">>,

       <<"H1 = qlc:q([{X} || X <- [], X > 1]),
          H = qlc:q([{X} || X <- H1, X < 10]),
          {list, []} = i(H),
          [] = qlc:e(H)">>,

       <<"L = [1,2,3],
          QH1 = qlc:q([{X} || X <- L, X > 1]),
          QH2 = qlc:q([{X} || X <- QH1]),
          [{{2}},{{3}}] = qlc:e(QH2),
          {list,{list,{list,L},_},_} = i(QH2)">>,

       <<"H = qlc:q([X || X <- qlc:q([Y || Y <- qlc:q([Z || Z <-[1,2,1]])])]),
          {list, [1,2,1]} = i(H),
          [1,2,1] = qlc:eval(H)">>,

       <<"%% Used to replace empty ETS tables with [], but that won't work.
          E = ets:new(apa,[]),
          QH1 = qlc:q([{X} || X <- ets:table(E), X > 1]),
          QH2 = qlc:q([{X} || X <- QH1], cache),
          [] = qlc:e(QH2),
          {qlc,_,[{generate,_,{table,{ets,table,_}}}],[]} = i(QH2),
          ets:delete(E)">>,

       <<"Q1 = qlc:q([W || W <- [a,b]]),
          Q2 = qlc:q([Z || Z <- qlc:sort([55296,56296,57296])], unique),
          Q3 = qlc:q([{X,Y} || X <- qlc:keysort([2], [{1,a}]),
                               Y <- qlc:append([Q1, Q2]),
                               X > Y]),
          {qlc, T1,
           [{generate, P1, {list, [{1,a}]}},
            {generate, P2, {append, [{list, [a,b]},
                                    {qlc, T2, [{generate, P3,
                                                {sort, {list,[55296,56296,57296]},[]}}],
                                     [{cache,ets},{unique,true}]}]}},F],
           []} = i(Q3, cache_all),
          {tuple, _, [{var,_,'X'}, {var,_,'Y'}]} = binary_to_term(T1),
          {var, _, 'X'} = binary_to_term(P1),
          {var, _, 'Y'} = binary_to_term(P2),
          {var, _, 'Z'} = binary_to_term(P3),
          {var, _, 'Z'} = binary_to_term(T2),
          {op, _, '>', {var, _, 'X'}, {var, _, 'Y'}} = binary_to_term(F),
          true = binary_to_list(<<
           \"beginV1=qlc:q([Z||Z<-qlc:sort([55296,56296,57296],[])],[{unique,true}]),\"
           \"qlc:q([{X,Y}||X<-[{1,a}],Y<-qlc:append([[a,b],V1]),X>Y])end\"
              >>) == format_info(Q3, true)">>,

       <<"Q1 = qlc:q([{X} || X <- qlc:q([X || X <- [a,b]])]),
          {qlc, _, [{generate, _, {list, [a,b]}}], []} = i(Q1),
          Q2 = qlc:q([X || X <- qlc:q([{X} || X <- [a,b]])]),
          {list,{list,[a,b]},_} = i(Q2),
          [{a},{b}] = qlc:eval(Q2)">>,

       <<"Q = qlc:keysort(2, [{1,a,b},{2,b,c},{3,4,c}]),
          {keysort,{list,[{1,a,b},{2,b,c},{3,4,c}]},2,[]} = i(Q),
          true = binary_to_list(<<
             \"qlc:keysort(2,[{1,a,b},{2,b,c},{3,4,c}],[])\">>) 
              == format_info(Q, true),
          [{3,4,c},{1,a,b},{2,b,c}] = qlc:e(Q)">>,

       <<"E = ets:new(foo, []),
          ets:insert(E, [{1},{2}]),
          Q = qlc_SUITE:default_table(E),
          {table,{'$MOD','$FUN',[]}} = i(Q),
          true = binary_to_list(<<\"'$MOD':'$FUN'()\">>) 
                == format_info(Q, true),
          true = ets:delete(E)">>,

       <<"\"[]\" = qlc:info([], flat),
          \"[]\" = qlc:info([]),
          \"[]\" = qlc:info([], {flat, true})">>,

       <<"H = qlc:q([{X} || X <- [a,b]]),
         \"ets:match_spec_run([a,b],ets:match_spec_compile(\" ++ _ =
                format_info(H, true),
         \"ets:match_spec_run([a,b],ets:match_spec_compile(\" ++ _ =
                format_info(H, false)">>,

       <<"H = qlc:q([{X} || X <- [a,b], begin true end]),
          true = binary_to_list(<<\"qlc:q([{X}||X<-[a,b],begintrueend])\">>) 
             == format_info(H, true),
          true = binary_to_list(<<\"qlc:q([{X}||X<-[a,b],begintrueend])\">>)
             == format_info(H, false)">>,

       <<"H = qlc:q([A || {A} <- [{1},{2}], (A =:= 2) andalso true]),
          {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} = 
             qlc:info(H, {format,abstract_code})">>,

       <<"H = qlc:q([{X} || X <- qlc:q([{X} || X <- [a,b], begin true end],
                                       unique), 
                            begin true end]),
          true = binary_to_list(<< 
         \"beginV1=qlc:q([{X}||X<-[a,b],begintrueend],[{unique,true}]),\"
         \"qlc:q([{X}||X<-V1,begintrueend])end\">>) == 
              format_info(H, true),
          true = binary_to_list(<<
         \"qlc:q([{X}||X<-qlc:q([{X}||X<-[a,b],begintrueend],\"
         \"[{unique,true}]),begintrueend])\">>) == format_info(H, false)">>,

       <<"H0 = qlc:q([{V3} || V3 <- qlc:q([{V1} || V1 <- [a,b], 
                                                   begin true end], unique), 
                              begin true end]),
          H = qlc:sort(H0),
          true = binary_to_list(<<
          \"qlc:sort(qlc:q([{V3}||V3<-qlc:q([{V1}||\"
          \"V1<-[a,b],begintrueend],[{unique,true}]),begintrueend]),[])\">>) 
              == format_info(H, false),
          true = binary_to_list(<<
          \"beginV2=qlc:q([{V1}||V1<-[a,b],begintrueend],[{unique,true}]),\"
          \"V4=qlc:q([{V3}||V3<-V2,begintrueend]),qlc:sort(V4,[])end\">>) 
              == format_info(H, true)">>,

       <<"H0 = qlc:q([X || X <- [true], begin true end]),
          H1 = qlc:q([{X} || X <- [a,b], begin true end], 
                     [{unique,begin [T] = qlc:e(H0), T end}]),
          {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},
                  [{lc,_,{tuple,_,[{var,_,'X'}]},
                         [{generate,_,{var,_,'X'},
                                      {cons,_,{atom,_,a},_}},
                          {block, _, [{atom, _, true}]}]},
                   {cons,_,_,_}]} = i(H1, {format, abstract_code})">>,

       <<"E = ets:new(apa, [duplicate_bag]),
          true = ets:insert(E, [{1,a},{2,b},{3,c},{4,d}]),
          QH = qlc:q([X || {X,_} <- ets:tab2list(E), X > 2], unique),
          {qlc, _, [{generate, _, {list, _, _MS}}], [{unique, true}]} = 
                i(QH),
          [3,4] = lists:sort(qlc:e(QH)),
          ets:delete(E)">>,

       %% "Imported" variable.
       <<"F = fun(U) -> qlc:q([{X} || X <- [1,2,3,4,5,6], X > U]) end,
          QH = F(4),
          {call, _ ,
                {remote, _, {atom, _, ets},{atom, _, match_spec_run}},
                [{string, _, [1,2,3,4,5,6]},
                 {call, _,
                       _compile,
                       [{cons, _,
                              {tuple, _,
                                     [{atom, _,'$1'},
                                      {cons, _,
                                            {tuple,
                                                 _,
                                                [{atom, _,'>'},
                                                 {atom, _,'$1'},
                                                 {tuple,
                                                      _,
                                                     [{atom, _,const},
                                                      {integer, _,4}]}]},
                                            _},
                                      {cons, _, _, _}]},
                              {nil,_}}]}]} = i(QH, {format, abstract_code}),
          [{5},{6}] = qlc:e(QH),
          [{4},{5},{6}] = qlc:e(F(3))">>,

          <<"Fun = fun ?MODULE:i/2,
             L = [{#{k => #{v => Fun}}, Fun}],
             H = qlc:q([Q || Q <- L, Q =:= {#{k => #{v => Fun}}, Fun}]),
             L = qlc:e(H),
             {call,_,_,[{lc,_,{var,_,'Q'},
                         [{generate,_,_,_},
                          {op,_,_,_,_}]}]} =
                qlc:info(H, [{format,abstract_code}])">>

       ],
    run(Config, Ts),
    ok.

%% Nested QLC expressions. QLC expressions in filter and template.
nested_info(Config) when is_list(Config) ->
    Ts = [
       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q(
                [{X,R} || 
                    {X,_} <- qlc_SUITE:table(L, []),
                    begin % X imported
                        R = qlc:e(qlc:q([{X,Y} || {Y,_}
                                                    <- qlc_SUITE:table(L, []),
                                                  Y > X])),
                        true
                    end]),
          true = binary_to_list(<<
            \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\"
            \"beginR=qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),Y>X]))\"
            \",trueend])\">>) == format_info(Q, true),
          [{1,[{1,2},{1,3}]},{2,[{2,3}]},{3,[]}] = qlc:e(Q)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q( % X imported
                [{X,qlc:e(qlc:q([{X,Y} || {Y,_} <- qlc_SUITE:table(L, []),
                                          Y > X]))} || 
                    {X,_} <- qlc_SUITE:table(L, [])]),
          true = binary_to_list(<<
            \"qlc:q([{X,qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\"
            \"Y>X]))}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}])])\">>)
              == format_info(Q, true),
          [{1,[{1,2},{1,3}]},{2,[{2,3}]},{3,[]}] = qlc:e(Q)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q(
                [{X,R} || 
                    {X,_} <- qlc_SUITE:table(L, []),
                    begin % X imported
                        R = qlc:e(qlc:q([{X,Y} || {Y,_} 
                                                    <- qlc_SUITE:table(L, []),
                                                  Y =:= X])),
                        true
                    end]),
          true = binary_to_list(<<
            \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\"
            \"beginR=qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\"
            \"Y=:=X])),trueend])\">>) == format_info(Q, true),
          [{1,[{1,1}]},{2,[{2,2}]},{3,[{3,3}]}] = qlc:e(Q)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q(
                [{X, % X imported
                  qlc:e(qlc:q([{X,Y} || {Y,_} <- qlc_SUITE:table(L, []),
                                        Y =:= X]))} || 
                    {X,_} <- qlc_SUITE:table(L, [])]),
          true = binary_to_list(<<
            \"qlc:q([{X,qlc:e(qlc:q([{X,Y}||{Y,_}<-qlc_SUITE:table(L,[]),\"
            \"Y=:=X]))}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}])])\">>)
             == format_info(Q, true),
          [{1,[{1,1}]},{2,[{2,2}]},{3,[{3,3}]}] = qlc:e(Q)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q(
                [{X,R} || 
                    {X,_} <- qlc_SUITE:table(L, []),
                    begin
                        R = qlc:e(qlc:q([Y || Y <- [X]])),
                        true
                    end]),
          true = binary_to_list(<<
            \"qlc:q([{X,R}||{X,_}<-qlc_SUITE:the_list([{1,a},{2,b},{3,c}]),\"
            \"beginR=qlc:e(qlc:q([Y||Y<-[X]])),trueend])\">>)
             == format_info(Q, true),
          [{1,[1]},{2,[2]},{3,[3]}] = qlc:e(Q)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          Q = qlc:q(
                [{X,qlc:e(qlc:q([Y || Y <- [X]]))} || 
                    {X,_} <- qlc_SUITE:table(L, [])]),
          true = binary_to_list(<<
            \"qlc:q([{X,qlc:e(qlc:q([Y||Y<-[X]]))}||{X,_}<-qlc_SUITE:\"
            \"the_list([{1,a},{2,b},{3,c}])])\">>) == format_info(Q, true),
          [{1,[1]},{2,[2]},{3,[3]}] = qlc:e(Q)">>,
          
       <<"L = [{1,a},{2,b}],
          Q = qlc:q(
                [{X,Y} ||
                    {X,_} <- qlc_SUITE:table(L, []),
                    {Y,_} <- qlc:q(
                               [{Z,V} || 
                                   {Z,_} <- qlc_SUITE:table(L, []),
                                   {V} <- qlc:q(
                                              [{W} || W 
                                                  <- qlc_SUITE:table(L, [])])
                                      ])
                       ]),
          true = binary_to_list(<<
           \"beginV1=qlc:q([{W}||W<-qlc_SUITE:the_list([{1,a},{2,b}])]),\"
           \"V2=qlc:q([{Z,V}||{Z,_}<-qlc_SUITE:the_list([{1,a},{2,b}]),\"
           \"{V}<-V1]),qlc:q([{X,Y}||{X,_}<-qlc_SUITE:the_list([{1,a},\"
           \"{2,b}]),{Y,_}<-V2])end\">>) == format_info(Q, true),
          [{1,1},{1,1},{1,2},{1,2},{2,1},{2,1},{2,2},{2,2}] = qlc:e(Q)">>

       ],
    run(Config, Ts),
    ok.


%% Lookup keys. Mostly test of patterns.
lookup1(Config) when is_list(Config) ->
    Ts = [
       <<"etsc(fun(E) ->
                Q = qlc:q([A || {A=3} <- ets:table(E)]),
                [3] = qlc:eval(Q),
                [3] = lookup_keys(Q) 
          end, [{1},{2},{3},{4}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([A || {A=3} <- ets:table(E)],{max_lookup,0}),
                [3] = qlc:eval(Q),
                false = lookup_keys(Q) 
          end, [{1},{2},{3},{4}])">>,

       <<"%% The lookup and max_lookup options interact.
          etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E),
                                (X =:= 1) or (X =:= 2)],
                          [{lookup,true},{max_lookup,1}]),
                {'EXIT', {no_lookup_to_carry_out, _}} = (catch qlc:e(Q))
         end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{A,B,C,D} || {A,B}={C,D} <- ets:table(E)]),
                [{1,2,1,2},{3,4,3,4}] = lists:sort(qlc:eval(Q)),
                false = lookup_keys(Q)
          end, [{1,2},{3,4}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E)]),
                [{1,1,1},{2,2,2}] = lists:sort(qlc:eval(Q)),
                false = lookup_keys(Q)
          end, [{1,2},{2,2},{1,1}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E),
                                      (D =:= 2) or (B =:= 1)],
                          {max_lookup,infinity}),
                [{1,1,1},{2,2,2}] = qlc:eval(Q),
                [1,2] = lookup_keys(Q)
         end, [{1,2},{2,2},{1,1}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E),
                                      (D =:= 2) xor (B =:= 1)]),
                [{1,1,1},{2,2,2}] = qlc:eval(Q),
                [1,2] = lookup_keys(Q)
         end, [{1,2},{2,2},{1,1}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([3 || {{[3,4],apa}} <- ets:table(E)]),
                [3] = qlc:e(Q),
                [{[3,4],apa}] = lookup_keys(Q)
        end, [{{[4,3],foo}},{{[3,4],apa}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([3 || {3} <- ets:table(E)]),
                [3] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{2},{3},{4}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{X,Y,Z} || {{X,_},Y,Y={_,Z},X,Y} <- ets:table(E)]),
                [] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{{1,1},1,{1,1},1,1}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X,X=2} <- ets:table(E)]),
                [2] = qlc:e(Q),
                [2] = lookup_keys(Q)
        end, [{2,2},{3,3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{_,3}={4,_}=X} <- ets:table(E)]),
                [{4,3}] = qlc:e(Q),
                [{4,3}] = lookup_keys(Q)
        end, [{{2,3}},{{4,3}}])">>,

       <<"U = 17.0,
          etsc(fun(E) ->
                Q = qlc:q([X || {_=X=_} <- ets:table(E)]),
                [U] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{U},{U+U,U}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{X,Y,Z,W} || {X=Y}=Z={V=W} <- ets:table(E), 
                                        V == {h,g}]),
                [{{h,g},{h,g},{{h,g}},{h,g}}] = qlc:e(Q),
                [{h,g}] = lookup_keys(Q)
        end, [{h,g},{{h,g}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{C,Y,Z,X} || {{X=Y}=Z}={{A=B}=C} <- ets:table(E), 
                                        A == a, B =/= c]),
                [{{a},a,{a},a}] = qlc:e(Q),
                [{a}] = lookup_keys(Q)
        end, [{{1}},{{a}}])">>,

       <<"etsc(fun(E) ->
               Q = qlc:q([{A,D,Y,X} || 
                             {{A={B=C}},{D={C}}} = {X,Y} <- ets:table(E),
                             [] == B]),
                [{{[]},{[]},{{[]}},{{[]}}}] = qlc:e(Q),
                [{{[]}}] = lookup_keys(Q)
        end, [{{{[]}},{{[]}}}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X}=X <- ets:table(E)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{1},{a}])">>,
        %% {warnings,[{{2,37},qlc,nomatch_pattern}]}},
        []},

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X=X,Y=Y}={Y=Y,X=X} <- ets:table(E), 
                                {} == X]),
                [{}] = qlc:e(Q),
                [{}] = lookup_keys(Q)
        end, [{{},{}},{[],[]}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {3+4=7,X} <- ets:table(E), 
                                X =:= 3+997]),
                [1000] = qlc:e(Q),
                [7] = lookup_keys(Q)
        end, [{7,1000},{8,1000}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{X, Y} || [X]=[Y] <- ets:table(E)]),
                [] = qlc:eval(Q),
                false = lookup_keys(Q)
        end, [{a}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || X={1,2,3,X,5} <- ets:table(E)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{a},{b}])">>,
        %% {warnings,[{{2,35},qlc,nomatch_pattern}]}},
        []},

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || X=[1,2,3,X,5] <- ets:table(E)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{a},{b}])">>,
        %% {warnings,[{{2,35},qlc,nomatch_pattern}]}},
        []},

       <<"etsc(fun(E) ->
                Q = qlc:q([X || X = <<X>> <- ets:table(E)]),
                [] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{a},{b}])">>,

       <<"Tre = 3.0,
          etsc(fun(E) ->
                Q = qlc:q([{A,B} || {A,B}={{a,C},{a,C}} <- ets:table(E), 
                                    C =:= Tre]),
                [] = qlc:e(Q),
                [{a,Tre}] = lookup_keys(Q)
        end, [{a,b}])">>,

       <<"A = 3,
          etsc(fun(E) ->
                Q = qlc:q([X || X <- ets:table(E), A =:= element(1, X)]),
                [{3,3}] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{1,a},{3,3}])">>,

       <<"A = 3,
          etsc(fun(E) ->
                Q = qlc:q([X || X <- ets:table(E), A =:= erlang:element(1, X)]),
                [{3,3}] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{1,a},{3,3}])">>,

       <<"etsc(fun(E) ->
                A = 3,
                Q = qlc:q([X || X <- ets:table(E), 
                                A == element(1,X), 
                                element(1,X) =:= a]),
                [] = qlc:e(Q),
                [a] = lookup_keys(Q)
        end, [{a},{b},{c}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X = {[a,Z]}, 
                                  Z = [foo, {[Y]}], 
                                  Y = {{foo,[X]}}} <- ets:table(E)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{a,b,c},{d,e,f}])">>,
        %% {warnings,[{{2,34},qlc,nomatch_pattern}]}}
        []}

       ],
    run(Config, Ts),
    ok.

%% Lookup keys. Mostly test of filters.
lookup2(Config) when is_list(Config) ->
    Ts = [
       <<"%% Only guards are inspected. No lookup.
          etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E),
                                     ((Y = X) =:= 3)]),
                 {'EXIT', {{badmatch,4},_}} = (catch qlc:e(Q))
         end, [{3,3},{4,true}])">>,

       <<"%% Only guards are inspected. No lookup.
          etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E),
                                     Y = (X =:= 3)]),
                 {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q))
         end, [{false,3},{true,3}])">>,

       <<"%% Only guards are inspected. No lookup.
          etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E),
                                     Y = (X =:= 3)]),
                 {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q))
         end, [{3,true},{4,true}])">>,

       <<"%% Only guards are inspected. No lookup.
          E1 = ets:new(e, [ordered_set]),
          true = ets:insert(E1, [{1,1}, {2,2}, {3,3}, {4,4}, {5,5}]),
          E2 = ets:new(join, [ordered_set]),
          true = ets:insert(E2, [{true,1},{false,2}]),
          Q = qlc:q([{X,Z} || {_,X} <- ets:table(E1),
                              {Y,Z} <- ets:table(E2),
                              Y = (X =:= 3)]),
          {'EXIT', {{badmatch,false},_}} = (catch qlc:e(Q)),
          ets:delete(E1),
          ets:delete(E2)">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{A,B,D} || {A,B}={D,A} <- ets:table(E), 
                                      (A =:= 3) or (4 =:= D)]),
                [{3,3,3},{4,4,4}] = lists:sort(qlc:e(Q)),
                [3,4] = lookup_keys(Q)
        end, [{2,2},{3,3},{4,4}])">>,

       <<"etsc(fun(E) ->
               Q = qlc:q([X || {X,U} <- ets:table(E), X =:= U]),
               [1] = qlc:e(Q),
               false = lookup_keys(Q)
       end, [{1,1}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 
                                     {[X],4} =:= {[3],X}]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{1}, {2}])">>,
        %% {warnings,[{{3,46},qlc,nomatch_filter}]}},
        []},

       {cres,
        <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X == 1, X =:= 2]),
                [] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{1}, {2}])">>,
        %% {warnings,[{{3,43},qlc,nomatch_filter}]}},
        []},

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 
                                     {[X,Y],4} =:= {[3,X],X}]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{1}, {2}])">>,
        %% {warnings,[{{3,48},qlc,nomatch_filter}]}},
        []},

       <<"etsc(fun(E) ->
                Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E), 
                                    ({X,3} =:= {Y,Y}) or (X =:= 4)]),
                [{3,3},{4,4}] = lists:sort(qlc:e(Q)),
                [3,4] = lookup_keys(Q)
        end, [{2,2},{3,3},{4,4},{5,5}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E), {[X]} =:= {[3,4]}]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{[3]},{[3,4]}])">>,
        %% {warnings,[{{2,61},qlc,nomatch_filter}]}},
        []},

       <<"etsc(fun(E) ->
                U = 18, 
                Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), [X|a] =:= [3|U]]),
                [] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{2}, {3}])">>,

       <<"etsc(fun(E) ->
                U = 18, V = 19,
                Q = qlc:q([{X,Y} || {X=Y} <- ets:table(E), 
                                    [X|V] =:= [3|U+1]]),
                [{3,3}] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{2},{3}])">>,

       <<"%% Blocks are not handled.
          etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), begin X == a end]),
                [a] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{a},{b}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E), 
                                 (3 =:= X) or (X =:= 12), 
                                 (8 =:= X) or (X =:= 10)]),
                 [] = lists:sort(qlc:e(Q)),
                 false = lookup_keys(Q)
         end, [{2},{3},{4},{8}])">>,
        %% {warnings,[{{4,44},qlc,nomatch_filter}]}},
        []},

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E),
                                 ((3 =:= X) or (X =:= 12)) 
                                  and ((8 =:= X) or (X =:= 10))]),
                 [] = lists:sort(qlc:e(Q)),
                 false = lookup_keys(Q)
         end, [{2},{3},{4},{8}])">>,
        %% {warnings,[{{4,35},qlc,nomatch_filter}]}},
        []},

       <<"F = fun(U) ->
                Q = qlc:q([X || {X} <- [a,b,c], 
                                 X =:= if U -> true; true -> false end]),
                [] = qlc:eval(Q),
                false = lookup_keys(Q)
              end,
          F(apa)">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X=1,X} <- ets:table(E), X =:= 2]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
           end, [{1,1},{2,1}])">>,
        %% {warnings,[{{2,61},qlc,nomatch_filter}]}},
        []},

       <<"Two = 2.0,
          etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), X =:= Two]),
                [Two] = qlc:e(Q),
                [Two] = lookup_keys(Q)
        end, [{2.0},{2}])">>,

       <<"etsc(fun(E) ->
                %% This float is equal (==) to an integer. Not a constant!
                Q = qlc:q([X || {X} <- ets:table(E), X == {a,b,c,[2.0]}]),
                [_,_] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{{a,b,c,[2]}},{{a,b,c,[2.0]}}])">>,

       <<"%% Must _not_ regard floats as constants. Check imported variables
          %% in runtime.
          etsc(fun(E) -> 
                U = 3.0,
                QH = qlc:q([X || {X,_} <- ets:table(E), X =:= U]),
                [] = qlc:e(QH),
                [U] = lookup_keys(QH)
        end, [{1,a},{2,b},{3,c},{4,d}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                length(X) =:= 1]),
                [[1]] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{[1]},{[2,3]}])">>,

       <<"etsc(fun(E) ->
                A=3, 
                Q = qlc:q([X || {X,Y} <- ets:table(E), X =:= A, Y =:= 3]),
                [3] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{3,3},{4,3}])">>,

       <<"etsc(fun(E) ->
                A = 1,
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X =:= 1, <<X>> =:= <<A>>]),
                [1] = qlc:e(Q),
                [1] = lookup_keys(Q)
        end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), X == a]),
                [a] = qlc:e(Q),
                [a] = lookup_keys(Q)
        end, [{a},{b},{c}])">>,

       <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X}=Y <- ets:table(E), 
                                 element(2, Y) == b, 
                                 X =:= 1]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
        end, [{1,b},{2,3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), element(1,{X}) =:= 1]),
                [1] = qlc:e(Q),
                [1] = lookup_keys(Q)
        end, [{1}, {2}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 1 =:= element(1,{X})]),
                [1] = qlc:e(Q),
                [1] = lookup_keys(Q)
        end, [{1}, {2}])">>,

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E), 
                                 X =:= {1}, 
                                 element(1,X) =:= 2]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{{1}},{{2}}])">>,
        %% {warnings,[{{4,47},qlc,nomatch_filter}]}},
        []},

       {cres,
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E), 
                                 X =:= {1}, 
                                 element(1,X) =:= element(1, {2})]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{{1}},{{2}}])">>,
        %% {warnings,[{{4,47},qlc,nomatch_filter}]}},
        []},

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E),
                                element(1,X) =:= 1, X =:= {1}]),
                [{1}] = qlc:e(Q),
                [{1}] = lookup_keys(Q)
        end, [{{1}},{{2}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E),
                                {{element(1,element(1,{{1}}))}} =:= {X}]),
                [{1}] = qlc:e(Q),
                [{1}] = lookup_keys(Q)
        end, [{{1}},{{2}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || X <- ets:table(E),
                                {element(1,element(1, {{1}}))} =:= 
                                      {element(1,X)}]),
                [{1}] = qlc:e(Q),
                [1] = lookup_keys(Q)
        end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                (X =:= 1) and (Y =:= 2) 
                                    or (X =:= 3) and (Y =:= 4)]),
                [1,3] = lists:sort(qlc:e(Q)),
                [{1,2}, {3,4}] = lookup_keys(Q)
        end, [{{1,2}}, {{3,4}}, {{2,3}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,a}} <- ets:table(E), X =:= 3]),
                [3] = qlc:e(Q),
                [{3,a}] = lookup_keys(Q)
        end, [{{3,a}},{{3,b}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y},_Z} <- ets:table(E), 
                                X =:= 3, Y =:= a]),
                [3] = qlc:e(Q),
                [{3,a}] = lookup_keys(Q)
        end, [{{3,a},3}, {{4,a},3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y},_Z} <- ets:table(E), 
                                (X =:= 3) and (Y =:= a)
                                   or (X =:= 4) and (Y =:= a)]),
                [3,4] = qlc:e(Q),
                [{3,a}, {4,a}] = lookup_keys(Q)
        end, [{{3,a},3}, {{4,a},3}])">>,

       {cres, 
        <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X} <- ets:table(E), 
                                 (X =:= 3) and (X =:= a)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
         end, [{3}, {4}])">>,
        %% {warnings,[{{3,44},qlc,nomatch_filter}]}},
        []},

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                X =:= 3, ((Y =:= a) or (Y =:= b))]),
                [3,3] = qlc:e(Q),
                [{3,a},{3,b}] = lists:sort(lookup_keys(Q))
        end, [{{3,a}},{{2,b}},{{3,b}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X,Y} <- ets:table(E), 
                                ((X =:= 3) or (Y =:= 4))  and (X == a)]),
                [a] = qlc:e(Q),
                [a] = lookup_keys(Q)
        end, [{a,4},{3,3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {X,Y} <- ets:table(E), 
                                (X =:= 3) or ((Y =:= 4)  and (X == a))]),
                [3,a] = lists:sort(qlc:e(Q)),
                [3,a] = lookup_keys(Q)
        end, [{a,4},{3,3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                (X =:= 3) or ((Y =:= 4)  and (X == a))]),
                [3,a] = lists:sort(qlc:e(Q)),
                false = lookup_keys(Q)
        end, [{{3,a}},{{2,b}},{{a,4}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                ((X =:= 3) or (Y =:= 4))  and (X == a)]),
                [a] = lists:sort(qlc:e(Q)),
                [{a,4}] = lookup_keys(Q)
        end, [{{3,a}},{{2,b}},{{a,4}}])">>,

        <<"etsc(fun(E) ->
                NoAnswers = 3*3*3+2*2*2,
                Q = qlc:q([{X,Y,Z} || 
                              {{X,Y,Z}} <- ets:table(E), 
                              (((X =:= 4) or (X =:= 5)) and
                               ((Y =:= 4) or (Y =:= 5)) and
                               ((Z =:= 4) or (Z =:= 5))) or
                              (((X =:= 1) or (X =:= 2) or (X =:= 3)) and
                               ((Y =:= 1) or (Y =:= 2) or (Y =:= 3)) and
                               ((Z =:= 1) or (Z =:= 2) or (Z =:= 3)))],
                          {max_lookup, NoAnswers}),
                 {list, {table, _}, _} = i(Q),
                 [{1,1,1},{2,2,2},{3,3,3}] = lists:sort(qlc:e(Q)),
                 true = NoAnswers =:= length(lookup_keys(Q))
         end, [{{1,1,1}},{{2,2,2}},{{3,3,3}},{{3,3,4}},{{4,1,1}}])">>,

        <<"etsc(fun(E) ->
                Q = qlc:q([{X,Y,Z} || 
                              {{X,Y,Z}} <- ets:table(E), 
                              (((X =:= 4) or (X =:= 5)) and
                               ((Y =:= 4) or (Y =:= 5)) and
                               ((Z =:= 4) or (Z =:= 5))) or
                              (((X =:= 1) or (X =:= 2) or (X =:= 3)) and
                               ((Y =:= 1) or (Y =:= 2) or (Y =:= 3)) and
                               ((Z =:= 1) or (Z =:= 2) or (Z =:= 3)))],
                          {max_lookup, 10}),
                 [{1,1,1},{2,2,2},{3,3,3}] = lists:sort(qlc:e(Q)),
                 {table,{ets,table,[_,[{traverse,{select,_}}]]}} = i(Q)
         end, [{{1,1,1}},{{2,2,2}},{{3,3,3}},{{3,3,4}},{{4,1,1}}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || X={_,_,_} <- ets:table(E), 
                                element(1, X) =:= 3, element(2, X) == a]),
                [{3,a,s}] = qlc:e(Q),
                [3] = lookup_keys(Q)
        end, [{1,c,q},{2,b,r},{3,a,s}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([X || X <- ets:table(E), 
                                element(0, X) =:= 3]),
                [] = qlc:e(Q),
                false = lookup_keys(Q)
        end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                F = fun(_) -> 3 end, 
                %% No occurs check; X =:= F(X) is ignored.
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X =:= 3, X =:= F(X)]),
                {qlc,_,[{generate,_,{list,{table,_},_}},_],[]} = i(Q),
                [3] = lists:sort(qlc:e(Q)),
                [3] = lookup_keys(Q)
        end, [{2},{3},{4}])">>,

       <<"etsc(fun(E) ->
                A = a, B = a,
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                ((X =:= A) and (Y =:= B))
                                 or ((X =:= B) and (Y =:= A))]),
                [a] = qlc:e(Q),
                %% keys are usorted, duplicate removed:
                [{a,a}] = lookup_keys(Q) 
        end, [{{a,a}},{{b,b}}])">>,

       <<"etsc(fun(E) ->
                A = a, B = b,
                Q = qlc:q([X || {{X,Y}} <- ets:table(E), 
                                ((X =:= A) and (Y =:= B))
                                 or ((X =:= B) and (Y =:= A))]),
                [a,b] = lists:sort(qlc:e(Q)),
                [{a,b},{b,a}] = lookup_keys(Q)
        end, [{{a,b}},{{b,a}},{{c,a}},{{d,b}}])">>,

       %% The atom 'fail' is recognized - lookup.
       <<"etsc(fun(E) ->
                Q = qlc:q([A || {A} <- ets:table(E), 
                                (A =:= 2) 
                                    orelse fail
                                   ]),
                [2] = lists:sort(qlc:e(Q)),
                [2] = lookup_keys(Q)
           end, [{1},{2}])">>

       ],

    ok = run(Config, Ts),

    TsR = [
       %% is_record/2,3:
       <<"etsc(fun(E) ->
                Q = qlc:q([element(1, X) || X <- ets:table(E), 
                                            erlang:is_record(X, r, 2)]),
                 [r] = qlc:e(Q),
                 [r] = lookup_keys(Q)
         end, [{keypos,1}], [#r{}])">>,
       <<"etsc(fun(E) ->
                Q = qlc:q([element(1, X) || X <- ets:table(E), 
                                            is_record(X, r, 2)]),
                 [r] = qlc:e(Q),
                 [r] = lookup_keys(Q)
         end, [{keypos,1}], [#r{}])">>,
       {cres,
        <<"etsc(fun(E) ->
                Q = qlc:q([element(1, X) || X <- ets:table(E), 
                                            record(X, r)]),
                 [r] = qlc:e(Q),
                 [r] = lookup_keys(Q)
         end, [{keypos,1}], [#r{}])">>,
        {warnings,[{{4,45},erl_lint,{obsolete_guard,{record,2}}}]}},
       <<"etsc(fun(E) ->
                Q = qlc:q([element(1, X) || X <- ets:table(E), 
                                            erlang:is_record(X, r)]),
                 [r] = qlc:e(Q),
                 [r] = lookup_keys(Q)
         end, [{keypos,1}], [#r{}])">>,
       <<"etsc(fun(E) ->
                Q = qlc:q([element(1, X) || X <- ets:table(E), 
                                            is_record(X, r)]),
                 [r] = qlc:e(Q),
                 [r] = lookup_keys(Q)
         end, [{keypos,1}], [#r{}])">>

       ],

    ok = run(Config, <<"-record(r, {a}).\n">>, TsR),

    Ts2 = [
       <<"etsc(fun(E) ->
                 Q0 = qlc:q([X || 
                                X <- ets:table(E),
                                (element(1, X) =:= 1) or 
                                  (element(1, X) =:= 2)],
                           {cache,ets}),
                 Q = qlc:q([{X,Y} ||
                               X <- [1,2],
                               Y <- Q0]),
                 {qlc,_,[{generate,_,{list,[1,2]}},
                         {generate,_,{table,_}}], []} = i(Q),
                 [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = lists:sort(qlc:e(Q)),
                 [1,2] = lookup_keys(Q) 
          end, [{keypos,1}], [{1},{2}])">>,

       <<"etsc(fun(E) ->
                 Q0 = qlc:q([X || 
                                X <- ets:table(E),
                                (element(1, X) =:= 1) or 
                                  (element(1, X) =:= 2)]),
                 Q = qlc:q([{X,Y} ||
                               X <- [1,2],
                               Y <- Q0],
                           {cache,true}),
                 {qlc,_,[{generate,_,{list,[1,2]}},
                         {generate,_,{table,_}}],[]} = i(Q),
                 [1,2] = lookup_keys(Q) 
          end, [{keypos,1}], [{1},{2}])">>,

       %% One introduced QLC expression (join, ms), and the cache option.
       <<"%% Match spec and lookup. The lookup is done twice, which might
          %% be confusing...
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y} <- ets:table(E),
                                     (Y =:= 1) or (Y =:= 2)],
                                 []),
                       [{1,1},{1,2},{2,1},{2,2}] = qlc:e(Q),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{list,{table,_},_}}],[]} = i(Q),
                       [1,2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% The same as last example, but with cache. 
          %% No cache needed (always one lookup only).
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y} <- ets:table(E),
                                     (Y =:= 1) or (Y =:= 2)],
                                 [cache]),
                       [{1,1},{1,2},{2,1},{2,2}] = qlc:e(Q),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{list,{table,_},_}}],[]} = i(Q),
                       [1,2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,

       <<"%% And again, this time only lookup, no mach spec.
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     Y <- ets:table(E),
                                     (element(1, Y) =:= 1) 
                                      or (element(1, Y) =:= 2)],
                                 []),
                       [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = qlc:e(Q),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{table,_}}],[]} = i(Q),
                       [1,2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% As last one, but with cache.
          %% No cache needed (always one lookup only).
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     Y <- ets:table(E),
                                     (element(1, Y) =:= 1) 
                                      or (element(1, Y) =:= 2)],
                                 [cache]),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{table,_}}],[]} = i(Q),
                       [{1,{1}},{1,{2}},{2,{1}},{2,{2}}] = qlc:e(Q),
                       [1,2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,

       <<"%% Lookup only. No cache.
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y=2} <- ets:table(E)],
                                 []),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{table,_}}],[]} = i(Q),
                       [{1,2},{2,2}] = qlc:e(Q),
                       [2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% Lookup only. No cache.
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y=2} <- ets:table(E)],
                                 [cache]),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,{table,_}}],[]} = i(Q),
                       [{1,2},{2,2}] = qlc:e(Q),
                       [2] = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% Matchspec only. No cache.
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y} <- ets:table(E),
                                     Y > 1],
                                 []),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                               {generate,_,
                                {table,{ets,_,[_,[{traverse,_}]]}}}],[]} = 
                                       i(Q),
                       [{1,2},{1,3},{2,2},{2,3}] = lists:sort(qlc:e(Q)),
                       false = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% Matchspec only. Cache
          etsc(fun(E) ->
                       Q = qlc:q([{X,Y} ||
                                     X <- [1,2],
                                     {Y} <- ets:table(E),
                                     Y > 1],
                                 [cache]),
                       {qlc,_,[{generate,_,{list,[1,2]}},
                            {generate,_,{qlc,_,
                           [{generate,_,{table,{ets,_,[_,[{traverse,_}]]}}}],
                          [{cache,ets}]}}],[]} = i(Q),
                       [{1,2},{1,3},{2,2},{2,3}] = lists:sort(qlc:e(Q)),
                       false = lookup_keys(Q)
               end, [{keypos,1}], [{1},{2},{3}])">>,
       <<"%% An empty list. Always unique and cached.
          Q = qlc:q([X || {X} <- [], X =:= 1, begin X > 0 end],
                   [{cache,true},{unique,true}]),
          {qlc,_,[{generate,_,{list,[]}},_],[{unique,true}]} = i(Q),
          _ = qlc:info(Q),
          [] = qlc:e(Q)">>,
       <<"%% A list is always cached.
          Q = qlc:q([{X,Y} || Y <- [1,2], X <- [2,1,2]],
                    [cache]),
          {qlc,_,[{generate,_,{list,[1,2]}},
                  {generate,_,{list,[2,1,2]}}],[]} = i(Q),
          [{2,1},{1,1},{2,1},{2,2},{1,2},{2,2}] = qlc:e(Q)">>,
       <<"%% But a processed list is never cached.
          Q = qlc:q([{X,Y} || Y <- [1,2], X <- [2,1,2], X > 1],
                    [cache]),
          {qlc,_,[{generate,_, {list,[1,2]}},
                  {generate,_,{qlc,_,
                               [{generate,_,{list,{list,[2,1,2]},_}}],
                               [{cache,ets}]}}],[]} = i(Q),
          [{2,1},{2,1},{2,2},{2,2}] = qlc:e(Q)">>,
       <<"%% A bug fixed in R11B-2: coalescing simple_qlc:s works now.
          Q0 = qlc:q([X || {X} <- [{1},{2},{3}]],  {cache, ets}),
          Q1 = qlc:q([X || X <- Q0], {cache, list}),
          Q = qlc:q([{Y,X} || Y <- [1,2], X <- Q1, X < 2], {cache, list}),
          {qlc,_,[{generate,_,{list,_}},
                  {generate,_,{qlc,_,[{generate,_,{list,{list,_},_}}],
                               [{cache,ets}]}},_],[]} = i(Q),
          [{1,1},{2,1}] = qlc:e(Q)">>,
       <<"Q = qlc:q([{X,Y} || Y <- [1,2], X <- [1,2], X > 1],
                    [cache,unique]),
          {qlc,_,[{generate,_,{list,[1,2]}},
                  {generate,_,{qlc,_,
                               [{generate,_,{list,{list,[1,2]},_}}],
                               [{cache,ets},{unique,true}]}}],
           [{unique,true}]} = i(Q),
          [{2,1},{2,2}] = qlc:e(Q)">>,
       <<"L = [1,2,3],
          QH1 = qlc:q([{X} || X <- L, X > 1]),
          QH2 = qlc:q([{X} || X <- QH1, X > 0], [cache]),
          [{{2}},{{3}}] = qlc:e(QH2),
          {list,{list,{list,L},_},_} = i(QH2)">>,
       <<"L = [1,2,3,1,2,3],
          QH1 = qlc:q([{X} || X <- L, X > 1]),
          QH2 = qlc:q([{X} || X <- QH1, X > 0], [cache,unique]),
          [{{2}},{{3}}] = qlc:e(QH2),
          {qlc,_,[{generate,_,{list,{list,{list,L},_},_}}],
           [{unique,true}]} = i(QH2)">>

      ],

    ok = run(Config, Ts2),

    LTs = [
       <<"etsc(fun(E) ->
                       Q  = qlc:q([X || X <- ets:table(E), 
                                        element(1, X) =:= 1],
                                  {lookup,true}),
                       {table,L} = i(Q),
                       true = is_list(L),
                       [{1,a}] = qlc:e(Q),
                       [1] = lookup_keys(Q)
               end, [{1,a},{2,b}])">>,
       <<"%% No lookup, use the match spec for traversal instead.
          etsc(fun(E) ->
                       Q  = qlc:q([X || X <- ets:table(E), 
                                        element(1, X) =:= 1],
                                  {lookup,false}),
                       {table,{ets,table,_}} = i(Q),
                       [{1,a}] = qlc:e(Q),
                       false = lookup_keys(Q)
               end, [{1,a},{2,b}])">>,
       <<"%% As last one. {max_lookup,0} has the same effect.
          etsc(fun(E) ->
                       Q  = qlc:q([X || X <- ets:table(E), 
                                        element(1, X) =:= 1],
                                  {max_lookup,0}),
                       {table,{ets,table,_}} = i(Q),
                       [{1,a}] = qlc:e(Q),
                       false = lookup_keys(Q)
               end, [{1,a},{2,b}])">>

       ],

    ok = run(Config, LTs),

    ok.

%% Lookup keys. With records.
lookup_rec(Config) when is_list(Config) ->
    Ts = [
       <<"etsc(fun(E) ->
                Q = qlc:q([A || #r{a = A} <- ets:table(E), 
                                (A =:= 3) or (4 =:= A)]),
                [3] = qlc:e(Q),
                [3,4] = lookup_keys(Q)
            end, [{keypos,2}], [#r{a = a}, #r{a = 3}, #r{a = 5}])">>,

       {cres, 
        <<"etsc(fun(E) ->
                 Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E),
                                 (A =:= 3) or (4 =:= A)]),
                 [] = qlc:e(Q),
                 false = lookup_keys(Q)
             end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>,
        %% {warnings,[{{4,44},qlc,nomatch_filter}]}},
        []},

      <<"%% Compares an integer and a float.
         etsc(fun(E) ->
                Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E),
                                (A == 17) or (17.0 == A)]),
                [_] = qlc:e(Q),
                [_] = lookup_keys(Q)
            end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>,

      <<"%% Compares an integer and a float.
         %% There is a test in qlc_pt.erl (Op =:= '=:=', C1 =:= C2), but
         %% that case is handled in an earlier clause (unify ... E, E).
         etsc(fun(E) ->
                Q = qlc:q([A || #r{a = 17.0 = A} <- ets:table(E),
                                (A =:= 17) or (17.0 =:= A)]),
                [_] = qlc:e(Q),
                [_] = lookup_keys(Q)
            end, [{keypos,2}], [#r{a = 17.0}, #r{a = 3}, #r{a = 5}])">>,

      <<"%% Matches an integer and a float.
         etsc(fun(E) ->
                Q = qlc:q([A || #r{a = 17 = A} <- ets:table(E),
                                (A =:= 17) or (17.0 =:= A)]),
                [_] = qlc:e(Q),
                [_] = lookup_keys(Q)
            end, [{keypos,2}], [#r{a = 17}, #r{a = 3}, #r{a = 5}])">>,

      <<"etsc(fun(E) ->
                F = fun(_) -> 17 end,
                Q = qlc:q([A || #r{a = A} <- ets:table(E),
                                (F(A) =:= 3) and (A =:= 4)]),
                [] = qlc:e(Q),
                false = lookup_keys(Q) % F(A) could fail
            end, [{keypos,2}], [#r{a = 4}, #r{a = 3}, #r{a = 5}])">>,

      <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                #r{} == X]),
                [#r{}] = lists:sort(qlc:e(Q)),
                {call,_,_,[_,_]} = i(Q, {format, abstract_code}),
                [#r{}] = lookup_keys(Q)
        end, [{#r{}},{#r{a=foo}}])">>,

      <<"etsc(fun(E) ->
                Q = qlc:q([R#r.a || R <- ets:table(E), R#r.a =:= foo]),
                [foo] = qlc:e(Q),
                [_] = lookup_keys(Q)
        end, [{keypos,2}], [#r{a=foo}])">>
       ],
    run(Config, <<"-record(r, {a}).\n">>, Ts),
    ok.

%% Using indices for lookup.
indices(Config) when is_list(Config) ->
    Ts = [
       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2]),
                                       (element(2, X) =:= a)
                                           or (b =:= element(2, X))]),
          {list, {table,{qlc_SUITE,list_keys,[[a,b],2,L]}}, _MS} = i(QH),
          [1,2] = qlc:eval(QH)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2]),
                                       begin (element(2, X) =:= a)
                                           or (b =:= element(2, X)) end]),
          {qlc,_,[{generate,_,{table,{call,_,
                               {remote,_,_,{atom,_,the_list}},_}}},_],[]}
                  = i(QH),
          [1,2] = qlc:eval(QH)">>,

       <<"L = [{1,a,q},{2,b,r},{3,c,s}],
          QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, [2,3]),
                                       (element(3, X) =:= q)
                                           or (r =:= element(3, X))]),
          {list, {table,{qlc_SUITE,list_keys, [[q,r],3,L]}}, _MS} = i(QH),
          [1,2] = qlc:eval(QH)">>,

       <<"L = [{1,a,q},{2,b,r},{3,c,s}],
          QH = qlc:q([element(1, X) || X <- qlc_SUITE:table(L, 1, [2]),
                                       (element(3, X) =:= q)
                                           or (r =:= element(3, X))]),
          {qlc,_,[{generate,_,{table,{call,_,_,_}}},
                  _],[]} = i(QH),
          [1,2] = qlc:eval(QH)">>,

       <<"L = [{a,1},{b,2},{c,3}],
          QH = qlc:q([E || {K,I}=E <- qlc_SUITE:table(L, 1, [2]),
                           ((K =:= a) or (K =:= b) or (K =:= c))
                               and ((I =:= 1) or (I =:= 2))],
                     {max_lookup, 3}),
          {list, {table,{qlc_SUITE,list_keys,[[a,b,c],1,L]}}, _MS} = i(QH),
          [{a,1},{b,2}] = qlc:eval(QH)">>,

       <<"L = [{a,1},{b,2},{c,3}],
          QH = qlc:q([E || {K,I}=E <- qlc_SUITE:table(L, 1, [2]),
                           ((K =:= a) or (K =:= b) or (K =:= c))
                               and ((I =:= 1) or (I =:= 2))],
                     {max_lookup, 2}),
          {list, {table,{qlc_SUITE,list_keys, [[1,2],2,L]}}, _MS} = i(QH),
          [{a,1},{b,2}] = qlc:eval(QH)">>,

       <<"L = [{a,1,x,u},{b,2,y,v},{c,3,z,w}],
          QH = qlc:q([E || {K,I1,I2,I3}=E <- qlc_SUITE:table(L, 1, [2,3,4]),
                           ((K =/= a) or (K =/= b) or (K =/= c))
                             and ((I1 =:= 1) or (I1 =:= 2) or 
                                  (I1 =:= 3) or (I1 =:= 4))
                             and ((I2 =:= x) or (I2 =:= z)) 
                             and ((I3 =:= v) or (I3 =:= w))],
                     {max_lookup, 5}),
          {list, {table,{qlc_SUITE,list_keys, [[x,z],3,L]}}, _MS} = i(QH),
          [{c,3,z,w}] = qlc:eval(QH)">>

       ],
    run(Config, <<"-record(r, {a}).\n">>, Ts),
    ok.

%% Test the table/2 callback functions parent_fun and stop_fun.
pre_fun(Config) when is_list(Config) ->
    Ts = [
       <<"PF = process_flag(trap_exit, true),
          %% cursor: table killing parent
          L = [{1,a},{2,b},{3,c}],
          F1 = fun() ->
                   QH = qlc:q([element(1, X) || 
                                X <- qlc_SUITE:table_kill_parent(L, [2]),
                                (element(2, X) =:= a)
                                    or (b =:= element(2, X))]),
                   _ = qlc:info(QH),
                   _ = qlc:cursor(QH)
               end,
          Pid1 = spawn_link(F1),
          receive {'EXIT', Pid1, killed} ->
              ok
          end,
          timer:sleep(1),
          process_flag(trap_exit, PF)">>,

       <<"PF = process_flag(trap_exit, true),
          %% eval without cursor: table killing parent
          L = [{1,a},{2,b},{3,c}],
          F2 = fun() ->
                 QH = qlc:q([element(1, X) || 
                                X <- qlc_SUITE:table_kill_parent(L, [2]),
                                (element(2, X) =:= a)
                                    or (b =:= element(2, X))]),
                 _ = qlc:eval(QH)
               end,
          Pid2 = spawn_link(F2),
          receive {'EXIT', Pid2, killed} ->
              ok
          end,
          process_flag(trap_exit, PF)">>,

       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(1, X) || 
                        X <- qlc_SUITE:table_parent_throws(L, [2]),
                        (element(2, X) =:= a)
                            or (b =:= element(2, X))]),
          _ = qlc:info(QH),
          {throw,thrown} = (catch {any_term,qlc:cursor(QH)}),
          {throw,thrown} = (catch {any_term,qlc:eval(QH)})">>,

       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(1, X) || 
                        X <- qlc_SUITE:table_parent_exits(L, [2]),
                        (element(2, X) =:= a)
                            or (b =:= element(2, X))]),
          _ = qlc:info(QH),
          {'EXIT', {badarith,_}} = (catch qlc:cursor(QH)),
          {'EXIT', {badarith,_}} = (catch qlc:eval(QH))">>,

       <<"L = [{1,a},{2,b},{3,c}],
          QH = qlc:q([element(1, X) || 
                        X <- qlc_SUITE:table_bad_parent_fun(L, [2]),
                        (element(2, X) =:= a)
                            or (b =:= element(2, X))]),
          {'EXIT', {badarg,_}} = (catch qlc:cursor(QH)),
          {'EXIT', {badarg,_}} = (catch qlc:eval(QH))">>,

       <<"%% Very simple test of stop_fun.
          Ets = ets:new(apa, [public]),
          L = [{1,a},{2,b},{3,c}],
          H = qlc:q([X || {X,_} <- qlc_SUITE:stop_list(L, Ets)]),
          C = qlc:cursor(H),
          [{stop_fun,StopFun}] = ets:lookup(Ets, stop_fun),
          StopFun(),
          {'EXIT', {{qlc_cursor_pid_no_longer_exists, _}, _}} =
                  (catch qlc:next_answers(C, all_remaining)),
          ets:delete(Ets)">>

       ],
    
    run(Config, Ts),
    ok.

%% Lookup keys. With records.
skip_filters(Config) when is_list(Config) ->
    %% Skipped filters
    TsS = [
       %% Cannot skip the filter.
       <<"etsc(fun(E) ->
                H = qlc:q([X || X <- ets:table(E), 
                          (element(1, X) =:= 1) xor (element(1, X) =:= 1)]),
                [] = qlc:eval(H),
                [1] = lookup_keys(H)
               end, [{keypos,1}], [{1},{2}])">>,

       %% The filter can be skipped. Just a lookup remains.
       <<"etsc(fun(E) ->
                H = qlc:q([X || X <- ets:table(E), 
                          (element(1, X) =:= 1) or (element(1, X) =:= 1)]),
                [{1}] = qlc:eval(H),
                {table, _} = i(H),
                [1] = lookup_keys(H)
               end, [{keypos,1}], [{1},{2}])">>,

       %% safe_unify fails on 3 and <<X:32>>
       <<"etsc(fun(E) ->
                H = qlc:q([X || X <- ets:table(E), 
                     (element(1, X) =:= 1) and (3 =:= <<X:32>>)]),
                [] = qlc:eval(H),
                [1] = lookup_keys(H)
               end, [{keypos,1}], [{1},{2}])">>,

       %% Two filters are skipped.
       <<"etsc(fun(E) ->
                Q = qlc:q([{B,C,D} || {A={C},B} <- ets:table(E), 
                                      (A =:= {1}) or (A =:= {2}),
                                      (C =:= 1) or (C =:= 2),
                                      D <- [1,2]]),
                {qlc,_,[{generate,_,{table,_}},{generate,_,{list,[1,2]}}],[]}
                  = i(Q),
                [{1,1,1},{1,1,2},{2,2,1},{2,2,2}] = lists:sort(qlc:eval(Q)),
                [{1},{2}] = lookup_keys(Q)
         end, [{{1},1},{{2},2},{{3},3}])">>,

       <<"etsc(fun(E) ->
                Q = qlc:q([{B,C} || {A={C},B} <- ets:table(E), 
                                    (A =:= {1}) or (A =:= {2}),
                                    (C =:= 1) or (C =:= 2)]),
                {qlc,_,[{generate,_,{table,_}}],[]} = i(Q),
                [{1,1},{2,2}] = lists:sort(qlc:eval(Q)),
                [{1},{2}] = lookup_keys(Q)
         end, [{{1},1},{{2},2},{{3},3}])">>,

       %% Lookup. No match spec, no filter.
       <<"etsc(fun(E) ->
                Q = qlc:q([X || X <- ets:table(E), 
                               element(1, X) =:= 1]),
                {table, _} = i(Q),
                [{1}] = qlc:e(Q),
                [1] = lookup_keys(Q)
         end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                 Q = qlc:q([{X,Y} || X <- ets:table(E), 
                                     element(1, X) =:= 1,
                                     Y <- [1,2]]),
                 {qlc,_,[{generate,_,{table,_}},{generate,_,{list,_}}],[]}
                      = i(Q),
                 [{{1},1},{{1},2}] = lists:sort(qlc:e(Q)),
                 [1] = lookup_keys(Q)
         end, [{1},{2}])">>,

       <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X,Y} <- ets:table(E), 
                                 X =:= a, 
                                 X =:= Y]),
                 {list,{table,_},_} = i(Q),
                 [a] = qlc:e(Q),
                 [a] = lookup_keys(Q)
          end, [{a,a},{b,c},{c,a}])">>,

       %% The imported variable (A) is never looked up in the current
       %% implementation. This means that the first filter cannot be skipped;
       %% the constant 'a' is looked up, and then the first filter evaluates
       %% to false.
       <<"etsc(fun(E) ->
                 A = 3,
                 Q = qlc:q([X || X <- ets:table(E), 
                                 A == element(1,X),
                                 element(1,X) =:= a]),
                 [] = qlc:e(Q),
                 [a] = lookup_keys(Q)
          end, [{a},{b},{c}])">>,

       %% No lookup.
       {cres, 
        <<"etsc(fun(E) ->
                  Q = qlc:q([X || {X} <- ets:table(E), 
                                  X =:= 1, 
                                  X =:= 2]),
                  {table, _} = i(Q),
                  [] = qlc:e(Q),
                  false = lookup_keys(Q)
          end, [{1,1},{2,0}])">>,
        %% {warnings,[{{4,37},qlc,nomatch_filter}]}},
        []},

       <<"etsc(fun(E) ->
                 Q = qlc:q([{A,B,C} ||
                               {A} <- ets:table(E),
                               A =:= 1,
                               {B} <- ets:table(E),
                               B =:= 2,
                               {C} <- ets:table(E),
                               C =:= 3]),
                 {qlc,_,[{generate,_,{list,{table,_},_}},
                         {generate,_,{list,{table,_},_}},
                         {generate,_,{list,{table,_},_}}],[]} = i(Q),
                 [{1,2,3}] = qlc:e(Q),
                 [1,2,3] = lookup_keys(Q)
          end, [{0},{1},{2},{3},{4}])">>

           ],
    run(Config, TsS),

    Ts = [
       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X,_} <- ets:table(E),
                                 X =:= 2]),
                 {list,{table,_},_} = i(H),
                 [2] = qlc:e(H)
         end, [{1,a},{2,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X,_} <- ets:table(E),
                                 ((X =:= 2) or (X =:= 1)) and (X > 1)]),
                 {list,{table,_},_} = i(H),
                 [2] = qlc:e(H)
         end, [{1,a},{2,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X,Y} <- ets:table(E),
                                 (X =:= 2) and (Y =:= b)]),
                 {list,{table,_},_} = i(H),
                 [2] = qlc:e(H)
         end, [{1,a},{2,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || X <- ets:table(E),
                                 (element(1,X) =:= 2) and (X =:= {2,b})]),
                 {list,{table,_},_} = i(H),
                 [{2,b}] = qlc:e(H)
         end, [{1,a},{2,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([{X,Y,Z,W} || 
                               {X,Y} <- ets:table(E),
                               {Z,W} <- ets:table(E),
                               (Y =:= 3) or (Y =:= 4)]),
                 {qlc,_,[{generate,_,{table,{ets,table,_}}},
                         {generate,_,{table,{ets,table,_}}}],[]} = i(H),
                 [{a,3,a,3},{a,3,b,5}] = lists:sort(qlc:e(H))
         end, [{a,3},{b,5}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([{X,Y} || 
                               {X,Y=3} <- ets:table(E), % no matchspec
                               %% Two columns restricted, but lookup anyway
                               (X =:= a)]),
                 {qlc,_,[{generate,_,{table,_}}],[]} = i(H),
                 [{a,3}] = qlc:e(H)
         end, [{a,3},{b,4}])">>,

       <<"etsc(fun(E) ->
                 V = 3,
                 H = qlc:q([{X,Y} || 
                               {X,Y} <- ets:table(E),
                               (Y =:= V)]), % imported variable, no lookup
                 {table,{ets,table,_}} = i(H),
                 [{a,3}] = qlc:e(H)
         end, [{a,3},{b,4}])">>,

       <<"etsc(fun(E) ->
                 V = b,
                 H = qlc:q([{X,Y} || 
                               {X,Y} <- ets:table(E),
                               (X =:= V)]), % imported variable, lookup
                 {list,{table,_},_} = i(H),
                 [{b,4}] = qlc:e(H)
         end, [{a,3},{b,4}])">>,

       <<"H = qlc:q([{A,B} || {{A,B}} <- [{{1,a}},{{2,b}}],
                              A =:= 1,
                              B =:= a]),
              {list,{list,[_,_]},_} = i(H),
              [{1,a}] = qlc:e(H)">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([{A,B} || {{A,B}} <- ets:table(E),
                                     A =:= 1,
                                     B =:= a]),
                 {list,{table,_},_} = i(H),
                 [{1,a}] = qlc:e(H)
         end, [{{1,a}},{{2,b}}])">>,

       %% The filters are skipped, and the guards of the match specifications
       %% are skipped as well. Only the transformations of the matchspecs
       %% are kept.
       <<"etsc(fun(E1) ->
                 etsc(fun(E2) ->
                              H = qlc:q([{X,Y,Z,W} ||
                                             {X,_}=Z <- ets:table(E1),
                                             W={Y} <- ets:table(E2),
                                             (X =:= 1) or (X =:= 2),
                                             (Y =:= a) or (Y =:= b)]
                                         ,{lookup,true}
                                        ),
                              {qlc,_,[{generate,_,{list,{table,_},
                                                   [{{'$1','_'},[],['$_']}]}},
                                      {generate,_,{list,{table,_},
                                                   [{{'$1'},[],['$_']}]}}],[]}
                              = i(H),
                              [{1,a,{1,a},{a}},
                               {1,b,{1,a},{b}},
                               {2,a,{2,b},{a}},
                               {2,b,{2,b},{b}}] = qlc:e(H)
                      end, [{a},{b}])
         end, [{1,a},{2,b}])">>,

       %% The same example again, but this time no match specs are run.
       <<"fun(Z) -> 
              etsc(fun(E1) ->
                     etsc(fun(E2) ->
                                  H = qlc:q([{X,Y} ||
                                                 Z > 2,
                                                 X <- ets:table(E1),
                                                 Y <- ets:table(E2),
                                                 (element(1, X) =:= 1) or
                                                 (element(1, X) =:= 2),
                                                 (element(1, Y) =:= a) or
                                                 (element(1, Y) =:= b)]
                                             ,{lookup,true}
                                            ),
                                  {qlc,_,[_,{generate,_,{table,_}},
                                          {generate,_,{table,_}}],[]} = i(H),
                                  [{{1,a},{a}},
                                   {{1,a},{b}},
                                   {{2,b},{a}},
                                   {{2,b},{b}}] = qlc:e(H)
                          end, [{a},{b}])
             end, [{1,a},{2,b}])
         end(4)">>,

       %% Once again, this time with a join.
       <<"etsc(fun(E1) ->
                 etsc(fun(E2) ->
                              H = qlc:q([{X,Y,Z,W} ||
                                             {X,V}=Z <- ets:table(E1),
                                             W={Y} <- ets:table(E2),
                                             (X =:= 1) or (X =:= 2),
                                             (Y =:= a) or (Y =:= b),
                                             Y =:= V]
                                         ,[{lookup,true},{join,merge}]
                                        ),
                              {qlc,_,[{generate,_,{qlc,_,
                                [{generate,_,{qlc,_,[{generate,_,
                                    {keysort,{list,{table,_},_},2,[]}},
                                 _C1,_C2],[]}},
                                  {generate,_,
                                      {qlc,_,[{generate, _,
                                         {keysort,{list,{table,_},_},1,[]}},
                                              _C3],
                                       []}},
                                 _],
                                [{join,merge}]}},_],[]} = i(H),
                              [{1,a,{1,a},{a}},{2,b,{2,b},{b}}] = 
                                  lists:sort(qlc:e(H))
                      end, [{a},{b}])
         end, [{1,a},{2,b}])">>,

       %% Filters 2 and 3 are not skipped. 
       %% (Only one filter at a time is tried by the parse transform.)
       <<"etsc(fun(E) ->
                 H = qlc:q([X || {{A,B}=X,Y} <- ets:table(E), % no matchspec
                                     Y =:= 3,
                                     A =:= 1,
                                     B =:= a]),

                 {qlc,_,[{generate,_,{table,_}},_,_,_],[]}= i(H),
                 [{1,a}] = qlc:e(H)
         end, [{{1,a},3},{{2,b},4}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec
                                  (X =:= 3) and (X > 3)]),
                 {qlc,_,[{generate,_,{table,_}},_],[]} = i(H),
                 [] = qlc:e(H)
         end, [{3,a},{4,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec
                                 (X =:= 3) or true]),
                 {qlc,_,[{generate,_,{table,{ets,table,_}}},_],[]} = i(H),
                 [3,4] = lists:sort(qlc:e(H))
         end, [{3,a},{4,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec
                                 (X =:= 3) or false]),
                 {qlc,_,[{generate,_,{table,_}}],[]} = i(H),
                 [3] = lists:sort(qlc:e(H))
         end, [{3,a},{4,b}])">>,

       <<"etsc(fun(E) ->
                 H = qlc:q([X || {X=_,_} <- ets:table(E), % no matchspec
                                 (X =:= X) and (X =:= 3)]),
                 {qlc,_,[{generate,_,{table,_}}],[]} = i(H),
                 [3] = lists:sort(qlc:e(H))
         end, [{3,a},{4,b}])">>,

       %% The order of filters matters. A guard filter cannot be used
       %% unless there are no non-guard filter placed before the guard
       %% filter that uses the guard filter's generator. There is
       %% more examples in join_filter().
       <<"etsc(fun(E) ->
                 %% Lookup. 
                 Q = qlc:q([{A,B,A} ||
                               {A=_,B} <- ets:table(E), % no match spec
                               A =:= 1,
                               begin 1/B > 0 end]),
                 [{1,1,1}] = lists:sort(qlc:e(Q))
         end, [{1,1},{2,0}])">>,
       <<"etsc(fun(E) ->
                 %% No lookup. 
                 Q = qlc:q([{A,B,A} ||
                               {A=_,B} <- ets:table(E), % no match spec
                               begin 1/B > 0 end,
                               A =:= 1]),
                 {'EXIT', _} = (catch qlc:e(Q))
         end, [{1,1},{2,0}])">>,
       %% The same thing, with a match specification.
       <<"etsc(fun(E) ->
                 Q = qlc:q([{A,B,A} ||
                               {A,B} <- ets:table(E), % match spec
                               A < 2,
                               begin 1/B > 0 end]),
                 [{1,1,1}] = lists:sort(qlc:e(Q))
         end, [{1,1},{2,0}])">>,
       <<"etsc(fun(E) ->
                 Q = qlc:q([{A,B,A} ||
                               {A,B} <- ets:table(E), % match spec
                               begin 1/B > 0 end,
                               A < 2]),
                 {'EXIT', _} = (catch qlc:e(Q))
         end, [{1,1},{2,0}])">>,
       %% More examples, this time two tables.
       <<"etsc(fun(E) ->
                 Q = qlc:q([{A,B,C,D} ||
                               {A,B} <- ets:table(E), % match spec
                               A < 2,
                               {C,D} <- ets:table(E),
                               begin 1/B > 0 end, %\"invalidates\" next filter
                               C =:= 1,
                               begin 1/D > 0 end]),
                 {qlc,_,[{generate,_,{table,{ets,table,_}}},
                         {generate,_,{table,{ets,table,_}}},
                         _,_,_],[]} = i(Q),
                 [{1,1,1,1}] = lists:sort(qlc:e(Q))
         end, [{1,1},{2,0}])">>,
       <<"etsc(fun(E) ->
              Q = qlc:q([{A,B,C,D} ||
                            {A,B} <- ets:table(E),
                            {C,D} <- ets:table(E),
                            begin 1/B > 0 end, % \"invalidates\" two filters
                            A < 2,
                            C =:= 1,
                            begin 1/D > 0 end]),
              {qlc,_,[{generate,_,{table,{ets,table,_}}},
                      {generate,_,{table,{ets,table,_}}},_,_,_,_],[]} = i(Q),
              {'EXIT', _} = (catch qlc:e(Q))
         end, [{1,1},{2,0}])">>,
      <<"%% There are objects in the ETS table, but none passes the filter.
         %% F() would not be run if it did not \"invalidate\" the following
         %% guards. 
         etsc(fun(E) ->
                      F = fun() -> [foo || A <- [0], 1/A] end,
                      Q1 = qlc:q([X || {X} <- ets:table(E),
                                       F(), % \"invalidates\" next guard
                                       X =:= 17]),
                      {'EXIT', _} = (catch qlc:e(Q1))
              end, [{1},{2},{3}])">>,
       <<"%% The last example works just like this one:
          etsc(fun(E) ->
                      F = fun() -> [foo || A <- [0], 1/A] end,
                      Q1 = qlc:q([X || {X} <- ets:table(E),
                                       F(),
                                       begin X =:= 17 end]),
                      {'EXIT', _} = (catch qlc:e(Q1))
              end, [{1},{2},{3}])">>

          ],
    run(Config, Ts),

    ok.


%% ets:table/1,2.
ets(Config) when is_list(Config) ->
    Ts = [
       <<"E = ets:new(t, [ordered_set]),
          true = ets:insert(E, [{1},{2}]),
          {'EXIT', _} = 
              (catch qlc:e(qlc:q([X || {X} <- ets:table(E, bad_option)]))),
          {'EXIT', _} = 
              (catch qlc:e(qlc:q([X || {X} <- ets:table(E,{traverse,bad})]))),
          All = [{'$1',[],['$1']}],
          TravAll = {traverse,{select,All}},
          [_, _] = qlc:e(qlc:q([X || {X} <- ets:table(E, TravAll)])),
          [_, _] = qlc:e(qlc:q([X || {X} <- ets:table(E,{traverse,select})])),
          [1,2] = 
             qlc:e(qlc:q([X || {X} <- ets:table(E, {traverse, first_next})])),
          [2,1] = 
             qlc:e(qlc:q([X || {X} <- ets:table(E, {traverse, last_prev})])),
          {table,{ets,table,[_,[{traverse,{select,_}},{n_objects,1}]]}} = 
              i(qlc:q([X || {X} <- ets:table(E, {n_objects,1})])),
          {qlc,_,[{generate,_,{table,{ets,table,[_,{n_objects,1}]}}},_],[]} =
              i(qlc:q([X || {X} <- ets:table(E,{n_objects,1}), 
                                   begin (X >= 1) or (X < 1) end])),
          {qlc,_,[{generate,_,{table,{ets,table,[_]}}},_],[]} = 
              i(qlc:q([X || {X} <- ets:table(E), 
                                   begin (X >= 1) or (X < 1) end])),
          ets:delete(E)">>,

       begin
       MS = ets:fun2ms(fun({X,Y}) when X > 1 -> {X,Y} end),
       [<<"E = ets:new(apa,[]),
           true = ets:insert(E, [{1,a},{2,b},{3,c}]),
           MS =  ">>, io_lib:format("~w", [MS]), <<",
           Q = qlc:q([X || {X,_} <- ets:table(E, {traverse, {select, MS}}), 
                           X =:= 1]),
           R = qlc:e(Q),
           ets:delete(E),
           [] = R">>]
       end,

       <<"E2 = ets:new(test, [bag]),
          Ref = make_ref(),
          true = ets:insert(E2, [{Ref,Ref}]),
          Q2 = qlc:q([{Val1} ||
                         {Ref1, Val1} <- ets:table(E2),
                         Ref1 =:= Ref]),
          S = qlc:info(Q2),
          true = is_list(S),
          [{Ref}] = qlc:e(Q2),
          ets:delete(E2)">>

       ],
    
    run(Config, Ts),
    ok.

%% dets:table/1,2.
dets(Config) when is_list(Config) ->
    dets:start(),
    T = t,
    Fname = filename(T, Config),
    Ts = [
       [<<"T = t, Fname = \"">>, Fname, <<"\",
           file:delete(Fname),
           {ok, _} = dets:open_file(T, [{file,Fname}]),
           ok = dets:insert(T, [{1},{2}]),
           {'EXIT', _} = 
              (catch qlc:e(qlc:q([X || {X} <- dets:table(T, bad_option)]))),
           {'EXIT', _} = 
              (catch qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,bad})]))),
           {'EXIT', _} = 
              (catch 
               qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,last_prev})]))),
           All = [{'$1',[],['$1']}],
           TravAll = {traverse,{select,All}},
           [_,_] = qlc:e(qlc:q([X || {X} <- dets:table(T, TravAll)])),
           [_,_] = qlc:e(qlc:q([X || {X} <- dets:table(T,{traverse,select})])),
           [_,_] = 
             qlc:e(qlc:q([X || {X} <- dets:table(T, {traverse, first_next})])),
           {table,{dets,table,[T,[{traverse,{select,_}},{n_objects,1}]]}} =
               i(qlc:q([X || {X} <- dets:table(T, {n_objects,1})])),
           {qlc,_,[{generate,_,{table,{dets,table,[t,{n_objects,1}]}}},_],[]}=
               i(qlc:q([X || {X} <- dets:table(T,{n_objects,1}), 
                             begin (X >= 1) or (X < 1) end])),
           {qlc,_,[{generate,_,{table,{dets,table,[_]}}},_],[]} = 
               i(qlc:q([X || {X} <- dets:table(T), 
                             begin (X >= 1) or (X < 1) end])),
           H = qlc:q([X || {X} <- dets:table(T, {n_objects, default}),
                           begin (X =:= 1) or (X =:= 2) or (X =:= 3) end]),
           [1,2] = lists:sort(qlc:e(H)),
           {qlc,_,[{generate,_,{table,_}},_],[]} = i(H),

           H2 = qlc:q([X || {X} <- dets:table(T), (X =:= 1) or (X =:= 2)]),
           [1,2] = lists:sort(qlc:e(H2)),
           {list,{table,_},_} = i(H2),
           true = binary_to_list(<<
            \"ets:match_spec_run(lists:flatmap(fun(V)->dets:lookup(t,V)end,\"
            \"[1,2]),ets:match_spec_compile([{{'$1'},[],['$1']}]))\">>)
                   == format_info(H2, true),

           H3 = qlc:q([X || {X} <- dets:table(T), (X =:= 1)]),
           [1] = qlc:e(H3),
           {list,{table,_},_} = i(H3),

           ok = dets:close(T),
           file:delete(\"">>, Fname, <<"\"),
           ok">>],

       begin
       MS = ets:fun2ms(fun({X,Y}) when X > 1 -> {X,Y} end),
       [<<"T = t, Fname = \"">>, Fname, <<"\",
           {ok, _} = dets:open_file(T, [{file,Fname}]),
           MS =  ">>, io_lib:format("~w", [MS]), <<",
           ok = dets:insert(T, [{1,a},{2,b},{3,c}]),
           Q = qlc:q([X || {X,_} <- dets:table(T, {traverse, {select, MS}}), 
                           X =:= 1]),
           R = qlc:e(Q),
           ok = dets:close(T),
           file:delete(\"">>, Fname, <<"\"),
           [] = R">>]
       end,

       [<<"T = t, Fname = \"">>, Fname, <<"\",
           {ok, _} = dets:open_file(T, [{file,Fname}]),
           Objs = [{X} || X <- lists:seq(1,10)],
           ok = dets:insert(T, Objs),
           {ok, Where} = dets:where(T, {2}),
           ok = dets:close(T),
           qlc_SUITE:crash(Fname, Where),

           {ok, _} = dets:open_file(T, [{file,Fname}]),
           HT = qlc:q([X || {X} <- dets:table(T, {traverse, first_next})]),
           {'EXIT',{error,{{bad_object,_},_}}} = (catch qlc:e(HT)),
           _ = dets:close(T),

           {ok, _} = dets:open_file(T, [{file,Fname}]),
           HMS = qlc:q([X || {X} <- dets:table(T, {traverse, select})]),
           {error,{{bad_object,_},_}} = qlc:e(HMS),
           _ = dets:close(T),

           {ok, _} = dets:open_file(T, [{file,Fname}]),
           HLU = qlc:q([X || {X} <- dets:table(T), X =:= 2]),
           {error,{{bad_object,_},_}} = qlc:e(HLU),
           _ = dets:close(T),

           file:delete(Fname)">>]

       ],
    
    run(Config, Ts),
    _ = file:delete(Fname),
    ok.


%% The 'join' option (any, lookup, merge, nested_loop). Also cache/unique.
join_option(Config) when is_list(Config) ->
    Ts = [
       <<"Q1 = qlc:q([X || X <- [1,2,3]],{join,merge}),
          {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:info(Q1)}),
          {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:e(Q1)}),

          Q2 = qlc:q([X || X <- [1,2,3], X > 1],{join,merge}),
          {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:info(Q2)}),
          {'EXIT', {no_join_to_carry_out,_}} = (catch {foo, qlc:e(Q2)}),

          Q3 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{a},{b},{c}],
                         X =:= Y],
                     {join, merge}),

          {1,0,0,2} = join_info(Q3),
          [] = qlc:e(Q3),

          Q4 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{a},{b},{c}],
                         X > Y],
                     {join, lookup}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q4)}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q4)}),

          Q5 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         X == Y],
                     {join, merge}),
          [{3,3}] = qlc:e(Q5),

          Q6 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         X == Y],
                     {join, lookup}),
          {'EXIT', {cannot_carry_out_join, _}} = (catch {foo, qlc:info(Q6)}),
          {'EXIT', {cannot_carry_out_join, _}} = (catch {foo, qlc:e(Q6)}),

          Q7 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         X == Y],
                     {join, nested_loop}),
          {0,0,1,0} = join_info(Q7),
          [{3,3}] = qlc:e(Q7),

          Q8 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         X =:= Y],
                     {join, nested_loop}),
          {0,0,1,0} = join_info(Q8),
          [{3,3}] = qlc:e(Q8),

          %% Only guards are inspected...
          Q9 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         begin X =:= Y end],
                     {join, nested_loop}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q9)}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q9)}),

          Q10 = qlc:q([{X,Y} ||
                         {X} <- [{1},{2},{3}],
                         {Y} <- [{3},{4},{5}],
                         X < Y],
                     {join, nested_loop}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:info(Q10)}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch {foo, qlc:e(Q10)}),

          F = fun(J) -> qlc:q([X || X <- [1,2]], {join,J}) end,
          {'EXIT', {no_join_to_carry_out, _}} = 
                (catch {foo, qlc:e(F(merge))}),
          {'EXIT', {no_join_to_carry_out, _}} = 
                (catch {foo, qlc:e(F(lookup))}),
          {'EXIT', {no_join_to_carry_out, _}} = 
                (catch {foo, qlc:e(F(nested_loop))}),
          [1,2] = qlc:e(F(any)),

          %% No join of columns in the same table.
          Q11 = qlc:q([{X,Y} || {a = X, X = Y} <- [{a,1},{a,a},{a,3},{a,a}]],
                      {join,merge}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q11)),
          Q12 = qlc:q([{X,Y} || {X = a, X = Y} <- [{a,1},{a,a},{a,3},{a,a}]],
                      {join,merge}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q12)),
          %% X and Y are \"equal\" (same constants), but must not be joined.
          Q13 = qlc:q([{X,Y} || {X,_Z} <- [{a,1},{a,2},{b,1},{b,2}],
                                {Y} <- [{a}],
                                (X =:= a) and (Y =:= b) or 
                                (X =:= b) and (Y =:= a)],
                     {join,merge}),
          {'EXIT', {no_join_to_carry_out, _}} = (catch qlc:e(Q13))

">>,

       <<"Q1 = qlc:q([X || X <- [1,2,3]], {lookup,true}),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q1)}),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q1)}),
          Q2 = qlc:q([{X,Y} || X <- [1,2,3], Y <- [x,y,z]], lookup),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q2)}),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q2)}),
          Q3 = qlc:q([X || {X} <- [{1},{2},{3}]], {lookup,true}),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:e(Q3)}),
          {'EXIT', {no_lookup_to_carry_out, _}} = (catch {foo, qlc:info(Q3)}),

          E1 = create_ets(1, 10),
          Q4 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], lookup),
          {match_spec, _} = strip_qlc_call(Q4),
          [{3,3}] = qlc:e(Q4),
          Q5 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], {lookup,false}),
          {table, ets, _} = strip_qlc_call(Q5),
          [{3,3}] = qlc:e(Q5),
          Q6 = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), X =:= 3], {lookup,any}),
          {match_spec, _} = strip_qlc_call(Q6),
          [{3,3}] = qlc:e(Q6),
          ets:delete(E1)">>

       ],
    run(Config, Ts),

    %% The 'cache' and 'unique' options of qlc/2 affects join.
    CUTs = [
       <<"L1 = [1,2],
          L2 = [{1,a},{2,b}],
          L3 = [{a,1},{b,2}],
          Q = qlc:q([{X,Y,Z} ||
                        Z <- L1,
                        {X,_} <- L2,
                        {_,Y} <- L3,
                        X =:= Y],
                    [cache, unique]),
          {qlc,_,
              [{generate,_,{list,L1}},
               {generate,_,{qlc,_,[{generate,_,
                      {qlc,_,[{generate,_,{keysort,{list,L2},1,[]}}],[]}},
                                {generate,_,{qlc,_,
                              [{generate,_,{keysort,{list,L3},2,[]}}],[]}},_],
                            [{join,merge},{cache,ets},{unique,true}]}},_],
              [{unique,true}]} = i(Q),
          [{1,1,1},{2,2,1},{1,1,2},{2,2,2}] = qlc:e(Q)">>,
       <<"L1 = [1,2],
          L2 = [{1,a},{2,b}],
          L3 = [{a,1},{b,2}],
          Q = qlc:q([{X,Y,Z} ||
                        Z <- L1,
                        {X,_} <- L2,
                        {_,Y} <- L3,
                        X =:= Y],
                    []),
          Options = [{cache_all,ets}, unique_all],
          {qlc,_,[{generate,_,{qlc,_,[{generate,_,{list,L1}}],
                               [{unique,true}]}},
                  {generate,_,{qlc,_,
                              [{generate,_,{qlc,_,[{generate,_,
                                 {keysort,{qlc,_,[{generate,_,{list,L2}}],
                                           [{cache,ets},{unique,true}]},
                                          1,[]}}],[]}},
                               {generate,_,{qlc,_,
                                    [{generate,_,{keysort,
                                      {qlc,_,[{generate,_,{list,L3}}],
                                       [{cache,ets},{unique,true}]},
                                                  2,[]}}],[]}},_],
                              [{join,merge},{cache,ets},{unique,true}]}},
                  _],[{unique,true}]} = i(Q, Options),
          [{1,1,1},{2,2,1},{1,1,2},{2,2,2}] = qlc:e(Q, Options)">>
       ],
    run(Config, CUTs),

    ok.

%% Various aspects of filters and join.
join_filter(Config) when is_list(Config) ->
    Ts = [
      <<"E1 = create_ets(1, 10),
          Q = qlc:q([X || {X,_} <- ets:table(E1),
                          begin A = X * X end, % ej true (?)
                          X >= A]),
          {'EXIT', _} = (catch qlc:e(Q)),
          ets:delete(E1)">>,

      %% The order of filters matters. See also skip_filters().
      <<"Q = qlc:q([{X,Y} || {X,Y} <- [{a,1},{b,2}], 
         {Z,W} <- [{a,1},{c,0}], 
         X =:= Z,
         begin Y/W > 0 end]),
         [{a,1}] = qlc:e(Q)">>,
      <<"Q = qlc:q([{X,Y} || {X,Y} <- [{a,1},{b,2}], 
         {Z,W} <- [{a,1},{c,0}], 
         begin Y/W > 0 end,
         X =:= Z]),
         {'EXIT', _} = (catch qlc:e(Q))">>,

      <<"etsc(fun(E1) ->
                   etsc(fun(E2) ->
                             F = fun() -> [foo || A <- [0], 1/A] end,
                             Q1 = qlc:q([X || {X} <- ets:table(E1),
                                              {Y} <- ets:table(E2),
                                              F(), % invalidates next filter
                                              X =:= Y]),
                              {qlc,_,[{generate,_,{table,{ets,table,_}}},
                                      {generate,_,{table,{ets,table,_}}},_,_],
                              []} = i(Q1),
                             {'EXIT', _} = (catch qlc:e(Q1))
                        end, [{1},{2},{3}])
              end, [{a},{b},{c}])">>

    ],
    run(Config, Ts),
    ok.

%% Lookup join.
join_lookup(Config) when is_list(Config) ->
    Ts = [
       <<"E1 = create_ets(1, 10),
          E2 = create_ets(5, 15),
          Q = qlc:q([{X,Y} || {_,Y} <- ets:table(E2),
                              {X,_} <- ets:table(E1),
                              X =:= Y], [{join,lookup}]),
          {0,1,0,0} = join_info_count(Q),
          R = qlc:e(Q),
          ets:delete(E1),
          ets:delete(E2),
          [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = lists:sort(R)">>,

       <<"E1 = create_ets(1, 10),
          E2 = create_ets(5, 15),
          F = fun(J) -> qlc:q([{X,Y} || {X,_} <- ets:table(E1),
                                        {_,Y} <- ets:table(E2),
                                        X =:= Y], {join, J})
              end,
          Q = F(lookup),
          {0,1,0,0} = join_info_count(Q),
          R = qlc:e(Q),
          ets:delete(E1),
          ets:delete(E2),
          [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = lists:sort(R)">>,

       <<"etsc(fun(E1) ->
                  E2 = qlc_SUITE:table([{1,a},{a},{1,b},{b}], 2, []),
                  Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), % (1)
                                      {_,Z} <- E2,   % (2)
                                      (Z =:= Y) and (X =:= a) 
                                      or
                                      (Z =:= Y) and (X =:= b)]),
                  %% Cannot look up in (1) (X is keypos). Can look up (2).
                  %% Lookup-join: traverse (1), look up in (2).
                  {0,1,0,0} = join_info_count(Q),
                  [{a,a},{b,a}] = qlc:e(Q)
               end, [{a,a},{b,a},{c,3},{d,4}])">>,

       <<"%% The pattern {X,_} is used to filter out looked up objects.
          etsc(fun(E) ->
                       Q = qlc:q([X || {X,_} <- ets:table(E), 
                                       Y <- [{a,b},{c,d},{1,2},{3,4}], 
                                       X =:= element(1, Y)]),
                       {0,1,0,0} = join_info_count(Q),
                       [1] = qlc:e(Q)
               end, [{1,2},{3}])">>,

       <<"E = ets:new(e, [bag,{keypos,2}]),
          L = lists:sort([{a,1},{b,1},{c,1},{d,1},
                          {aa,2},{bb,2},{cc,2},{dd,2}]),
          true = ets:insert(E, L ++ [{aaa,1,1},{bbb,2,2},{ccc,3,3}]),
          Q = qlc:q([Z || {_,Y}=Z <- ets:table(E),
                          {X} <- [{X} || X <- lists:seq(0, 10)],
                          X =:= Y]),
          {0,1,0,0} = join_info_count(Q),
          R = qlc:e(Q),
          ets:delete(E),
          L = lists:sort(R)">>,

       <<"E = ets:new(e, [bag,{keypos,2}]),
          L = lists:sort([{a,1},{b,1},{c,1},{d,1},
                          {aa,2},{bb,2},{cc,2},{dd,2}]),
          true = ets:insert(E, L ++ [{aaa,1,1},{bbb,2,2},{ccc,3,3}]),
          Q = qlc:q([Z || {X} <- [{X} || X <- lists:seq(0, 10)],
                          {_,Y}=Z <- ets:table(E),
                          X =:= Y]),
          {0,1,0,0} = join_info_count(Q),
          R = qlc:e(Q),
          ets:delete(E),
          L = lists:sort(R)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{b,1},{c,3}],
                            {Y,YY} <- qlc_SUITE:table_lookup_error([{1,a}]),
                            X =:= Y],
                        {join,lookup}),
          {error, lookup, failed} = qlc:e(Q)">>,

       <<"E = create_ets(1, 10),
          Q = qlc:q([{X,Y} ||
                        {X,_} <- ets:table(E),
                        {_,Y} <- qlc_SUITE:table_error([{a,1}], 1, err),
                        X =:= Y]),
          {0,1,0,0} = join_info_count(Q),
          err = qlc:e(Q),
          ets:delete(E)">>

          ],
    run(Config, Ts),
    ok.

%% Merge join.
join_merge(Config) when is_list(Config) ->
    Ts = [
       <<"Q = qlc:q([{X,Y} || {X} <- [], {Y} <- [{1}], X =:= Y], 
                    {join,merge}),
          [] = qlc:e(Q)
       ">>,

       <<"Q = qlc:q([{X,Y} || {X} <- [{1}], {Y} <- [], X =:= Y], 
                    {join,merge}),
          [] = qlc:e(Q)
       ">>,

       <<"Q = qlc:q([{X,Y} || {X} <- [{1},{1},{1}], 
                              {Y} <- [{1},{1},{1}], X =:= Y], 
                    {join,merge}),
          9 = length(qlc:e(Q))
       ">>,

       <<"%% Two merge joins possible.
          Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [{1,a},{1,b},{1,c}], 
                                  {Z,W} <- [{1,a},{1,b},{1,c}], 
                                  X =:= Z, 
                                  Y =:= W]),
          {qlc,_,[{generate,_,
                   {qlc,_,
                    [{generate,_,
                      {qlc,_,[{generate,_,{keysort,{list,_},C,[]}}],[]}},
                     {generate,_,
                      {qlc,_,[{generate,_,{keysort,{list,_},C,[]}}],[]}},
                     _],
                    [{join,merge}]}},
                  _,_],[]} = qlc:info(Q, {format,debug}),
          [{1,a,1,a},{1,b,1,b},{1,c,1,c}] = qlc:e(Q)">>,

       <<"%% As the last one, but comparison.
          Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [{1,a},{1,b},{1,c}], 
                                  {Z,W} <- [{1,a},{1,b},{1,c}], 
                                  X == Z, % skipped
                                  Y =:= W]),
          {qlc,_,[{generate,_,
                   {qlc,_,
                    [{generate,_,
                      {qlc,_,[{generate,_,{keysort,{list,_},1,[]}}],[]}},
                     {generate,_,
                      {qlc,_,[{generate,_,{keysort,{list,_},1,[]}}],[]}},
                     _],
                    [{join,merge}]}},
                  _],[]} = qlc:info(Q, {format,debug}),
          [{1,a,1,a},{1,b,1,b},{1,c,1,c}] = qlc:e(Q)">>,

       <<"%% This is no join.
          Q = qlc:q([{X,Y,Z,W} || {X,Y} <- [], {Z,W} <- [], 
                                  X =:= Y, Z =:= W]),
          {0,0,0,0} = join_info_count(Q)">>,

       <<"%% Used to replace empty ETS tables with [], but that won't work.
          E1 = ets:new(e1, []),
          E2 = ets:new(e2, []),
          Q = qlc:q([{X,Z,W} ||
                        {X, Z} <- ets:table(E1),
                        {W, Y} <- ets:table(E2),
                        X =:= Y],
                    {join, lookup}),
          [] = qlc:e(Q),
          ets:delete(E1),
          ets:delete(E2)">>,

       <<"Q = qlc:q([{X,Y} || {X} <- [{3},{1},{0}], 
                              {Y} <- [{1},{2},{3}], 
                              X =:= Y]),
          {1,0,0,2} = join_info_count(Q),
          [{1,1},{3,3}] = qlc:e(Q)">>,

       <<"QH = qlc:q([{X,Y,Z,W} || {X,Y} <- [{3,c},{2,b},{1,a}],
                                   {Z,W} <- [{2,b},{4,d},{5,e},{3,c}], 
                                   X =:= Z, 
                                   Y =:= W]),
          {1,0,0,2} = join_info_count(QH),
          [{2,b,2,b},{3,c,3,c}] = qlc:e(QH)">>,

       <<"%% QLC finds no join column at run time...
          QH = qlc:q([1 || X <- [{1,2,3},{4,5,6}], 
                           Y <- [{1,2},{3,4}], 
                           X =:= Y]),
          {0,0,0,0} = join_info_count(QH),
          [] = qlc:e(QH)">>,

       <<"QH = qlc:q([X || X <- [{1,2,3},{4,5,6}], 
                           Y <- [{1,2},{3,4}], 
                           element(1, X) =:= element(2, Y)]),
          {1,0,0,2} = join_info_count(QH),
          [{4,5,6}] = qlc:e(QH)">>,

       <<"Q = qlc:q([{A,X,Z,W} ||
                        A <- [a,b,c],
                        {X,Z} <- [{a,1},{b,4},{c,6}],
                        {W,Y} <- [{2,a},{3,b},{4,c}],
                        X =:= Y],
                   {cache, list}),
          _ = qlc:info(Q),
         [{a,a,1,2},{a,b,4,3},{a,c,6,4},{b,a,1,2},{b,b,4,3},
          {b,c,6,4},{c,a,1,2},{c,b,4,3},{c,c,6,4}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{X,Y} || 
                        {X,Z} <- [{a,1},{b,4},{c,6}],
                        {W,Y} <- [{2,a},{3,b},{4,c}],
                        Z > W,
                        X =:= Y],
                    {join,merge}),
          {qlc,_,[{generate,_,{qlc,_,
                              [{generate,_,
                                {qlc,_,[{generate,_,{keysort,_,1,[]}}],[]}},
                               {generate,_,
                                {qlc,_,[{generate,_,{keysort,_,2,[]}}],
                                 []}},_],[{join,merge}]}},
                  _,_],[]} = i(Q),
          [{b,b},{c,c}] = qlc:e(Q)">>,

       <<"E1 = create_ets(1, 10),
          E2 = create_ets(5, 15),
          %% A match spec.; Q does not see Q1 and Q2 as lookup-tables.
          Q1 = qlc:q([X || X <- ets:table(E1)]),
          Q2 = qlc:q([X || X <- ets:table(E2)]),
          F = fun(J) -> qlc:q([{X,Y} || X <- Q1,
                                        Y <- Q2,
                                        element(1,X) =:= element(1,Y)], 
                              [{join,J}])
              end,
          {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(F(lookup))),
          Q = F(merge),
          {1,0,0,2} = join_info(Q),
          R = lists:sort(qlc:e(Q)),
          ets:delete(E1),
          ets:delete(E2),
          true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {X,X})] =:= R
       ">>,

       <<"E1 = create_ets(1, 10),
          E2 = create_ets(5, 15),
          Q = qlc:q([{X,Y} || X <- ets:table(E1),
                              Y <- ets:table(E2),
                              element(1,X) =:= element(1,Y)], 
                    [{join,merge}]),
          {1,0,0,2} = join_info(Q),
          R = lists:sort(qlc:e(Q)),
          ets:delete(E1),
          ets:delete(E2),
          true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {X,X})] =:= R
       ">>,

       <<"E1 = create_ets(1, 10),
          E2 = create_ets(5, 15),
          Q1 = qlc:q([Y || X <- ets:table(E1), begin Y = {X}, true end]),
          %% A match spec.; Q does not see Q2 as a lookup-table.
          %%
          %% OTP-6673: lookup join is considered but since there is no
          %% filter that can do the job of Q2, lookup join is not an option..
          Q2 = qlc:q([{X} || X <- ets:table(E2)]),
          F = fun(J) ->
                      qlc:q([{X,Y} || X <- Q1,
                                      Y <- Q2,
                                      element(1,X) =:= element(1,Y)],
                            [{join,J}])
              end,
          {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(F(lookup))),
          Q = F(any),
          {1,0,0,2} = join_info(Q),
          R = lists:sort(qlc:e(Q)),
          ets:delete(E1),
          ets:delete(E2),
          true = [{Y,Y} || X <- lists:seq(5, 10), {} =/= (Y = {{X,X}})] =:= R
       ">>,

       <<"L1 = [{1,a},{2,a},{1,b},{2,b},{1,c},{2,c}],
          L2 = [{b,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      Q = qlc:q([{XX,YY} ||
                                    {X,XX} <- L1,
                                    {YY,Y} <- L2,
                                    X == Y],
                                {join,J}),
                      qlc:q([{XX1,YY1,XX2,YY2} ||
                                {XX1,YY1} <- Q,
                                {XX2,YY2} <- Q])
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          true = lists:sort(qlc:e(Qm)) =:= lists:sort(qlc:e(Qn))">>,

       <<"L1 = [{{1,a},2},{{3,c},4}],
          L2 = [{a,{1,a}},{c,{4,d}}],
          Q = qlc:q([{X,Y} || {X,_} <- L1,
                              {_,{Y,Z}} <- L2,
                              X == {Y,Z}
                           ]),
          {qlc,_,[{generate,_,{qlc,_,
                   [{generate,_,
                     {qlc,_,[{generate,_,{keysort,{list,L1},1,[]}}],[]}},
                    {generate,_,
                     {qlc,_,[{generate,_,{keysort,{list,L2},2,[]}}],[]}},
                    _],
                   [{join,merge}]}}],[]}  = i(Q),
          [{{1,a},1}] = qlc:e(Q)">>,

       <<"etsc(fun(E1) ->
                   etsc(fun(E2) ->
                      Q = qlc:q([{X,Y} || {X,Y} <- ets:table(E1), % (1)
                                          {Z} <- ets:table(E2),   % (2)
                                            (Z =:= X) and 
                                            (Y =:= a) and 
                                            (X =:= Y) or 
                                          (Y =:= b) and 
                                          (Z =:= Y)]),
                      %% Cannot look up in (1) (X is keypos). Can look up (2).
                      %% Lookup join not possible (cannot look up in (1)).
                      %% Merge join is possible (after lookup in (2)).
                      {1,0,0,2} = join_info_count(Q),
                      {qlc,_,
                       [{generate,_,
                         {qlc,_,[{generate,_,
                                  {qlc,_,[{generate,_,
                                           {keysort,
                                            {table,{ets,table,_}},
                                                  2,[]}},_C1],[]}},
                                 {generate,_,
                                  {qlc,_,[{generate,_,
                                           {keysort,{table,_},1,[]}},_C2],
                                   []}},
                                 _],[{join,merge}]}},_],[]} = i(Q),
                      [{a,a}] = qlc:e(Q)
                   end, [{a}])
                end, [{a,1},{a,a},{b,1},{b,2}])">>,

       <<"Q = qlc:q([{G1,G2} || 
                        G1<- [{1}],
                        G2 <- [{1}],
                        element(1, G1) =:= element(1, G2)]),
          {1,0,0,2} = join_info(Q),
          [{{1},{1}}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{X,Y} || 
                         X <- [{1}], 
                         Y <- [{1}], 
                         element(1, X) =:= element(1, Y)], 
                     {join,merge}),
          {1,0,0,2} = join_info(Q),
          [{{1},{1}}] = qlc:e(Q)">>,

       <<"%% Generator after the join-filter.
          Q = qlc:q([Z || 
                        {X} <- [{1},{2},{3}], 
                        {Y} <- [{2},{3},{4}], 
                        X =:= Y, 
                        Z <- [1,2]]),
          {qlc,_,
           [{generate,_,{qlc,_,
                [{generate,_,{qlc,_,
                    [{generate,_,{keysort,{list,[{1},{2},{3}]},1,[]}}],[]}},
                {generate,_,{qlc,_,
                    [{generate,_,{keysort,{list,_},1,[]}}],[]}},_],
                [{join,merge}]}}, _,{generate,_,{list,_}}],[]} = i(Q),
          [1,2,1,2] = qlc:e(Q)">>,

       <<"%% X and W occur twice in the pattern of the extra join handle.
          Q = qlc:q([{Z,W} ||
                        {X,Z,X} <- [{1,2,1},{1,2,2}],
                        {W,Y,W} <- [{a,1,a}],
                        X =:= Y]),
          [{2,a}] = qlc:e(Q)">>

          ],
    run(Config, Ts),

    %% Small examples. Returning an error term.
    ETs = [
       <<"F = fun(M) ->
                  qlc:q([{XX,YY} ||
                         {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}],
                         {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b},{2,bb},{2,bbb},
                                    {3,c},{3,cc}],
                          X =:= Y],
                        {join,M})
              end,
          R = qlc:e(F(nested_loop)),
          R = qlc:e(F(merge))">>,

       <<"F = fun(M) ->
                qlc:q([{XX,YY} ||
                       {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}],
                       {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b},{2,bb},{2,bbb},
                                  {4,d}],
                        X =:= Y],
                      {join,M})
              end,
          R = qlc:e(F(nested_loop)),
          R = qlc:e(F(merge))">>,

       <<"Q = qlc:q([{XX,YY} ||
                        {XX,X} <- [{b,1},{c,3}],
                        {Y,YY} <- [{1,a}],
                        X =:= Y],
                    {join,merge}),
          [{b,a}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{b,1},{c,3}],
                            {Y,YY} <- qlc_SUITE:table_error([{1,a}], 1, err),
                            X =:= Y],
                        {join,merge}),
              err = qlc:e(Q)">>,
           
       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{aa,1}],
                            {Y,YY} <- [{1,a}],
                            X =:= Y],
                        {join,merge}),
              [{a,a},{aa,a}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- qlc_SUITE:table_error([{a,1},{aa,1}], 
                                                             2, err),
                            {Y,YY} <- [{1,a}],
                            X =:= Y],
                        {join,merge}),
              err = qlc:e(Q)">>,
           
       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1}],
                            {Y,YY} <- [{1,a},{1,aa}],
                            X =:= Y],
                        {join,merge}),
              [{a,a},{a,aa}]= qlc:e(Q)">>,
           
       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- qlc_SUITE:table_error([{a,1}], 2, err),
                            {Y,YY} <- [{1,a},{1,aa}],
                            X =:= Y],
                        {join,merge}),
          C = qlc:cursor(Q),
          [{a,a}] = qlc:next_answers(C, 1),
          qlc:delete_cursor(C),
          err = qlc:e(Q)">>,

       <<"F = fun(M) ->
                    qlc:q([{XX,YY} ||
                               {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}],
                               {Y,YY} <- [{0,a},{1,a},{1,aa},{2,b},
                                          {2,bb},{2,bbb}],
                            X =:= Y],
                          {join,M})
              end,
              %% [{a,a},{a,aa},{b,b},{b,bb},{b,bbb},{bb,b},{bb,bb},{bb,bbb}]
              R = qlc:e(F(nested_loop)),
              R = qlc:e(F(merge))">>,


       <<"F = fun(M) ->
                   qlc:q([{XX,YY} ||
                          {XX,X} <- [{a,1},{b,2},{bb,2},{c,3},{cc,3}],
                          {Y,YY} <- qlc_SUITE:table_error([{0,a},{1,a},{1,aa},
                                                           {2,b},{2,bb},
                                                           {2,bbb}], 
                                                          1, err),
                           X =:= Y],
                         {join,M})
              end,
              %% [{a,a},{a,aa},{b,b},{b,bb},{b,bbb},{bb,b},{bb,bb},{bb,bbb}]
              err = qlc:e(F(nested_loop)),
              err = qlc:e(F(merge))">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- qlc_SUITE:table_error([], 2, err),
                            {Y,YY} <- [{2,b},{3,c}],
                            X =:= Y],
                        {join,merge}),
              err = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{c,3}],
                            {Y,YY} <- [{2,b},{3,c}],
                            X =:= Y],
                        {join,merge}),
              [{c,c}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{aa,1}],
                            {Y,YY} <- [{1,a},{1,aa}],
                            X =:= Y],
                        {join,merge}),
              [{a,a},{a,aa},{aa,a},{aa,aa}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{b,2}],
                            {Y,YY} <- [{1,a},{1,aa}],
                            X =:= Y],
                        {join,merge}),
              [{a,a},{a,aa}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{b,2}],
                            {Y,YY} <- qlc_SUITE:table_error([{1,a},{1,aa}], 
                                                            1, err),
                            X =:= Y],
                        {join,merge}),
              err = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                            {XX,X} <- [{a,1},{b,2}],
                            {Y,YY} <- [{1,a},{1,aa},{1,aaa},{1,aaaa}],
                            X =:= Y],
                        {join,merge}),
              [{a,a},{a,aa},{a,aaa},{a,aaaa}]= qlc:e(Q)">>,

       <<"Q = qlc:q([{element(1, X), element(2, Y)} ||
                            X <- [{a,1},{aa,1}],
                            Y <- [{1,a},{1,aa}],
                            element(2, X) =:= element(1, Y)],
                        {join,merge}),
              [{a,a},{a,aa},{aa,a},{aa,aa}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{element(1, X), element(2, Y)} ||
                            X <- [{a,1},{aa,1}],
                            Y <- qlc_SUITE:table_error([], 1, err),
                            element(2, X) =:= element(1, Y)],
                        {join,merge}),
              err = qlc:e(Q)">>,

       <<"Q = qlc:q([{element(1, X), element(2, Y)} ||
                            X <- qlc_SUITE:table_error([{a,1}], 2, err),
                            Y <- [{2,b}],
                            element(2, X) =:= element(1, Y)],
                        {join,merge}),
              err = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                  {XX,X} <- [{1,a},{'1b',b},{2,b}],
                  {Y,YY} <- [{a,1},{b,'1b'},{c,1}],
                  X == Y],
              {join,merge}),
          [{1,1},{'1b','1b'},{2,'1b'}] = qlc:e(Q)">>,

       <<"Q = qlc:q([{XX,YY} ||
                  {XX,X} <- qlc_SUITE:table_error([{1,a},{'1b',b},{2,b}], 
                                                  2, err),
                  {Y,YY} <- [{a,1},{b,'1b'},{c,1}],
                  X == Y],
              {join,merge}),
          err = qlc:e(Q)">>

          ],
    run(Config, ETs),

    %% Mostly examples where temporary files are needed while merging.
    FTs = [
       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)],
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          true = qlc:e(Qm,{max_list_size, 0}) =:= qlc:e(Qn)">>,

       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)],
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
          Q = qlc:q([{XX,YY} ||
                        {XX,X} <- L1,
                        {Y,YY} <- L2,
                        X == Y],
                    {join,merge}),
          {error,_,{file_error,_,_}} = 
               qlc:e(Q, [{max_list_size,64*1024},{tmpdir,\"/a/b/c\"}])">>,

       <<"L1 = qlc_SUITE:table_error([{1,a},{2,a}], 2, err),
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          err = qlc:e(Qm, {max_list_size,64*1024}),
          err = qlc:e(Qn)">>,

       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)],
          L2 = qlc_SUITE:table_error([{a,Y} || Y <- lists:seq(1, 10000)], 
                                     1, err),
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          err = qlc:e(Qm, {max_list_size,64*1024}),
          err = qlc:e(Qn)">>,

       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)] ++ 
               [{'1b',b},{2,b}] ++ [{Y,d} || Y <- lists:seq(1, 2)],
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)] ++ 
               [{b,'1b'}] ++ [{c,1}] ++ [{d,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          true = lists:sort(qlc:e(Qm, {max_list_size,64*1024})) =:= 
                 lists:sort(qlc:e(Qn))">>,

       <<"F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- [{Y,a} || Y <- lists:seq(1, 2)],
                                {Y,YY} <- [{a,Y} || Y <- lists:seq(1,100000)],
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          true = qlc:e(Qm, {max_list_size,64*1024}) =:= qlc:e(Qn)">>,

       %% More than one join in one QLC expression.
       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)],
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      Q = qlc:q([{XX,YY} ||
                                    {XX,X} <- L1,
                                    {Y,YY} <- L2,
                                    X == Y,
                                    begin XX > 1 end,
                                    begin YY > 9999 end],
                                {join,J}),
                      qlc:q([{XX1,YY1,XX2,YY2} ||
                                {XX1,YY1} <- Q,
                                {XX2,YY2} <- Q])
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          R1 = lists:sort(qlc:e(Qm, {max_list_size,64*1024})),
          R2 = lists:sort(qlc:e(Qm, {max_list_size,1 bsl 31})),
          true = R1 =:= lists:sort(qlc:e(Qn)),
          true = R1 =:= R2">>,

       <<"L1 = [{Y,a} || Y <- lists:seq(1, 2)],
          L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
          F = fun(J) ->
                      Q = qlc:q([{XX,YY} ||
                                    {XX,X} <- L1,
                                    {Y,YY} <- L2,
                                    X == Y,
                                    begin XX > 1 end,
                                    begin YY > 9999 end],
                                {join,J}),
                      qlc:q([{XX1,YY1,XX2,YY2} ||
                                {XX1,YY1} <- Q,
                                {XX2,YY2} <- Q,
                                throw(thrown)])
              end,
          Qm = F(merge),
          thrown = (catch {any_term, qlc:e(Qm, {max_list_size,64*1024})})">>,

       <<"%% Bigger than 64*1024.
          T1 = {1, lists:seq(1, 20000)},
          L1 = [{a,T1},{b,T1}],
          L2 = [{T1,a},{T1,b}],
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          R = [{a,a},{a,b},{b,a},{b,b}],
          R = qlc:e(Qm, {max_list_size,64*1024}),
          R = qlc:e(Qn)">>,

       <<"%% Bigger than 64*1024. No temporary files.
          T1 = {1, lists:seq(1, 20000)},
          L1 = [{a,T1},{b,T1}],
          L2 = [{T1,a},{T1,b}],
          F = fun(J) ->
                      qlc:q([{XX,YY} ||
                                {XX,X} <- L1,
                                {Y,YY} <- L2,
                                X == Y],
                            {join,J})
              end,
          Qm = F(merge),
          Qn = F(nested_loop),
          R = [{a,a},{a,b},{b,a},{b,b}],
          R = qlc:e(Qm, {max_list_size,1 bsl 31}),
          R = qlc:e(Qn)">>


          ],
    run(Config, FTs),
    
    ok.

%% Merge join optimizations (avoid unnecessary sorting).
join_sort(Config) when is_list(Config) ->
    Ts = [
       <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}]),
          H1 = qlc:q([X || X <- H1_1], unique),
          H2 = qlc:keysort(2, [{1,2},{3,4}]),
          H3 = qlc:q([{X,Y} || {X,_,_} <- H1, 
                               {_,Y} <- H2, 
                               X =:= Y]),
          {1,0,0,2} = join_info(H3),
          [{4,4}] = qlc:e(H3)">>,

       <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}]),
          H1 = qlc:q([X || X <- H1_1], unique), % keeps the order
          H2 = qlc:keysort(2, [{1,2},{3,4}]),
          H3 = qlc:q([{X,Y} || {X,_,_} <- H1, % no extra keysort
                               {Y,_} <- H2,   % an extra keysort
                               X =:= Y]),
          {1,0,0,3} = join_info(H3),
          [{1,1}] = qlc:e(H3)">>,

       <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}], {tmpdir,\"\"}),
          H1 = qlc:q([X || X <- H1_1], unique),
          H2 = qlc:keysort(2, [{1,2},{3,4}]),
          H3 = qlc:q([{X,Y} || {_,X,_} <- H1, 
                               {_,Y} <- H2, 
                               X =:= Y]),
          {1,0,0,3} = join_info(H3),
          [{2,2}] = qlc:e(H3)">>,

       <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6}], {tmpdir,\"\"}),
          H1 = qlc:q([X || X <- H1_1], unique),
          H2 = qlc:keysort(2, [{1,2},{3,4}]),
          H3 = qlc:q([{X,Y} || {_,X,_} <- H1, 
                               {_,Y} <- H2, 
                               X =:= Y]),
          {1,0,0,3} = join_info(H3),
          [{2,2}] = qlc:e(H3)">>,

       <<"H1 = qlc:sort([{1,a},{2,b},{3,c}]),
          %% Since H1 is sorted it is also keysorted on the first column.
          Q = qlc:q([{X, Y} || {X,_} <- H1,
                               {Y} <- [{0},{1},{2}],
                               X == Y]),
          {1,0,0,1} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q)">>,

       <<"H1 = qlc:sort([{r,a,1},{r,b,2},{r,c,3}]),
          Q = qlc:q([{X, Y} || {r,_,X} <- H1, % needs keysort(3)
                               {Y} <- [{0},{1},{2}],
                               X == Y]),
          {1,0,0,2} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q)">>,

       <<"QH = qlc:q([X || X <- [{1,2,3},{4,5,6}], 
                           Y <- qlc:sort([{1,2},{3,4}]), 
                           element(1, X) =:= element(2, Y)]),
          {1,0,0,2} = join_info_count(QH),
          [{4,5,6}] = qlc:e(QH)">>,

       <<"H1_1 = qlc:keysort(1, [{1,2,3},{4,5,6},{1,2,3}]),
          H1 = qlc:q([X || X <- H1_1], unique),
          H2 = qlc:keysort(2, [{2,1},{3,4}]),
          H3 = qlc:q([{X,Y} || {X,_,_} <- H1, 
                               {_,Y} <- H2, 
                               X =:= Y]),
          H4 = qlc:keysort(1, [{1,2},{3,4},{4,a}]),
          H5 = qlc:q([{X,Y} || {X,_} <- H4,
                               {_,Y} <- H3,
                               X =:= Y]),
          {2,0,0,3} = join_info_count(H5),
          [{1,1},{4,4}]= qlc:e(H5)">>,

       <<"
          H1 = qlc:keysort(2, [{1,a,u},{2,b,k},{3,c,l}]),
          H2 = qlc:q([{a,X,Y,a} || {1,X,u} <- H1,
                                   {2,Y,k} <- H1]),
          %% Neither H1 nor H2 need to be key-sorted 
          %% (the columns are constant).
          H3 = qlc:q([{A,B,C,D,E,F,G,H} ||
                         {A,B,C,D} <- H2,
                         {E,F,G,H} <- H2,
                         A =:= H],
                     {join,merge}),
          {1,0,0,4} = join_info_count(H3),
          [{a,a,b,a,a,a,b,a}] = qlc:e(H3)">>,

       <<"%% Q1 is sorted on X or Y.
          Q1 = qlc:q([{X,Y} ||
                         {X,_} <- qlc:keysort(1, [{1,a},{2,b}]),
                         {_,Y} <- qlc:keysort(2, [{aa,11},{bb,22}]),
                         X < Y]),
          [{1,11},{1,22},{2,11},{2,22}] = qlc:e(Q1),
          Q = qlc:q([{X,Y} ||
                        {X,_} <- Q1, % no need to sort Q1
                        {Y} <- [{0},{1},{2},{3}],
                        X =:= Y]),
          {1,0,0,3} = join_info_count(Q),
          [{1,1},{1,1},{2,2},{2,2}] = qlc:e(Q)">>,

       <<"H1 = qlc:keysort([2], [{r,1},{r,2},{r,3}]),
          %% H1 is actually sorted, but this info is not captured.
          Q = qlc:q([{X, Y} || {r,X} <- H1,
                               {Y} <- [{0},{1},{2}],
                               X == Y]),
          {1,0,0,2} = join_info_count(Q),
          [{1,1},{2,2}] = qlc:e(Q)">>,

       <<"%% Two leading constants columns and sorted objects
          %% implies keysorted on column 3.
          H1 = qlc:sort(qlc:q([{a,X,Y} || {X,Y} <- [{1,2},{2,3},{3,3}]])),
          H2 = qlc:q([{X,Y} || 
                         {a,3,X} <- H1,
                         {a,2,Y} <- H1,
                         X =:= Y]),
          {1,0,0,0} = join_info_count(H2),
          [{3,3}] = qlc:e(H2)">>,

       <<"QH = qlc:q([{X,Y} || {X,Y} <- [{1,4},{1,3}],
                               {Z} <- [{1}],
                               X =:= Z, (Y =:= 3) or (Y =:= 4)]),
          {1,0,0,1} = join_info_count(QH),
          [{1,4},{1,3}] = qlc:e(QH)">>,

       <<"E = ets:new(join, [ordered_set]),
          true = ets:insert(E, [{1,a},{2,b},{3,c}]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E), % no need to sort
                               {Y} <- [{0},{1},{2}],
                               X == Y], {join,merge}),
          {1,0,0,1} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q),
          ets:delete(E)">>,

       <<"H1 = qlc:sort([{r,1,a},{r,2,b},{r,3,c}]),
          Q = qlc:q([{X, Y} || {r,X,_} <- H1, % does not need keysort(3)
                               {Y} <- [{0},{1},{2}],
                               X == Y]),
          {1,0,0,1} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q)">>,

       <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]),
          H2 = [{a},{b}],
          %% Several columns in different qualifiers have initial 
          %% constant columns.
          H3 = qlc:keysort(1,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]),
          Q = qlc:q([{r,X,Y,Z} || {r,X} <- H1,
                                  {Y} <- H2,
                                  {c1,c2,Z} <- H3,
                                  X =:= Z], {join,merge}),
          {1,0,0,3} = join_info(Q),
          [{r,1,a,1},{r,1,b,1},{r,2,a,2},{r,2,b,2},{r,3,a,3},{r,3,b,3}] =
              qlc:e(Q)">>,

       <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]),
          H2 = [{a},{b}],
          %% As the last one, but one keysort less.
          H3 = qlc:keysort(3,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]),
          Q = qlc:q([{r,X,Y,Z} || {r,X} <- H1,
                                  {Y} <- H2,
                                  {c1,c2,Z} <- H3,
                                  X =:= Z], {join,merge}),
          {1,0,0,2} = join_info(Q),
          [{r,1,a,1},{r,1,b,1},{r,2,a,2},{r,2,b,2},{r,3,a,3},{r,3,b,3}] =
              qlc:e(Q)">>,

       <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]),
          H2 = [{a},{b}],
          H3 = qlc:keysort(1,[{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]),
          %% One generator before the joined generators.
          Q = qlc:q([{r,X,Y,Z} || {Y} <- H2,
                                  {r,X} <- H1,
                                  {c1,c2,Z} <- H3,
                                  X =:= Z], {join,merge}),
          {1,0,0,3} = join_info(Q),
          [{r,1,a,1},{r,2,a,2},{r,3,a,3},{r,1,b,1},{r,2,b,2},{r,3,b,3}] = 
              qlc:e(Q)">>,

       <<"H1 = [{a,1},{b,2},{c,3},{d,4}],
          H2 = [{a},{b}],
          H3 = [{c1,c2,a},{foo,bar,b},{c1,c2,c},{c1,c2,d}],
          %% A couple of \"extra\" filters and generators.
          Q = qlc:q([{X,Y,Z} || {X,_} <- H1,
                                {Y} <- H2,
                                X > Y,
                                {c1,c2,Z} <- H3,
                                {W} <- [{a},{b}],
                                W > a,
                                X =:= Z]),
          {1,0,0,2} = join_info(Q),
          [{c,a,c},{c,b,c},{d,a,d},{d,b,d}] = qlc:e(Q)">>,

       <<"H1 = qlc:keysort(2,[{r,1},{r,2},{r,3}]),
          H2 = qlc:sort([{c1,c2,1},{foo,bar,2},{c1,c2,3},{c1,c2,2}]),
          %% H2 is sorted, no keysort necessary.
          %% This example shows that the 'filter-part' of the pattern
          %% ({c1,c2,Z}) should be evaluated _before_ the join.
          %% Otherwise the objects cannot be assumed to be keysort:ed on the
          %% third column (if merge join), and lookup-join would lookup
          %% more keys than necessary.
          Q = qlc:q([{r,X,Z} || {r,X} <- H1,
                                {c1,c2,Z} <- H2,
                                X =:= Z] ,{join,merge}),
          {1,0,0,1} = join_info(Q),
          [{r,1,1},{r,2,2},{r,3,3}] = qlc:e(Q)">>,

       <<"H1 = [{1,a},{2,b},{3,c}],
          H2 = [{0,0},{1,1},{2,2}],
          H3 = qlc:q([{A,C,D} ||
                         {A,_B} <- H1,
                         {C,D} <- H2,
                         A == D, C == D]),
          H4 = [{1,1},{2,2},{3,3}],
          H5 = qlc:q([{X,Y} ||
                         {X,_,_} <- H3, % no need to sort this one (merge join)
                         {_,Y} <- H4,
                         X == Y]),
          Q = qlc:q([{X,Y} ||
                        {X,_} <- H5, % no need to sort this one
                        {Y,_} <- H4,
                        X == Y]),
          {{3,0,0,4},{3,0,0,6}} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q)">>,

       <<"%% There is an extra test (_C1, element(1, X) =:= 1) that is not
          %% necessary since the match spec does the same check. This can be
          %% improved upon.
          Q = qlc:q([{X,Y} ||
                        X <- [{2},{1}],
                        element(1, X) =:= 1,
                        Y=_ <- [{2},{1}],
                        element(1, X) =:= element(1, Y)]),
          {qlc,_,
              [{generate,_,{qlc,_,
                              [{generate,_,{qlc,_,
                                             [{generate,_,{list,{list,_},_}},
                                              _C1],[]}},
                               {generate,_,{qlc,_,
                                             [{generate,_,{list,[{2},{1}]}},
                                              _C2],[]}},_],
                              [{join,merge}]}},_],[]} = i(Q),
          {1,0,0,0} = join_info_count(Q),
          [{{1},{1}}] = qlc:e(Q)">>,

       <<"etsc(fun(E) ->
                       L = [{a,b,a},{c,d,b},{1,2,a},{3,4,b}],
                       Q = qlc:q([P1 || {X,2,Z}=P1 <- ets:table(E), 
                                        Y <- L,
                                        X =:= 1,
                                        Z =:= a,
                                        P1 =:= Y, 
                                        X =:= element(1, Y)]),
                       {1,0,0,0} = join_info_count(Q),
                       [{1,2,a}] = qlc:e(Q)
               end, [{1,2,a},{3,4,b}])">>,

       %% Merge join on Z and element(3, Y). No need to sort!
       <<"etsc(fun(E) ->
                       L = [{a,b,a},{c,d,b},{1,2,a},{3,4,b}],
                       Q = qlc:q([P1 || {X,2,Z}=P1 <- ets:table(E), 
                                        Y <- L,
                                        (X =:= 1) or (X =:= 2),
                                        Z =:= a,
                                        P1 =:= Y, 
                                        X =:= element(1, Y)]),
                       {1,0,0,0} = join_info_count(Q),
                       [{1,2,a}] = qlc:e(Q)
               end, [{1,2,a},{3,4,b}])">>,

       <<"%% Y is constant as well as X. No keysort, which means that
          %% Y must be filtered before merge join.
          etsc(fun(E) ->
                       Q = qlc:q([X || {1,2}=X <- ets:table(E), 
                                       Y <- [{a,b},{c,d},{1,2},{3,4}], 
                                       X =:= Y, 
                                       element(1, X) =:= element(1, Y)]),
                       {1,0,0,0} = join_info_count(Q),
                       [{1,2}] = qlc:e(Q)
               end, [{1,2},{3,4}])">>

         ],
    run(Config, Ts),
    ok.

%% Join of more than two columns.
join_complex(Config) when is_list(Config) ->
    Ts = [{three,
           <<"three() ->
                  L = [],
                  Q = qlc:q([{X,Y,Z} || {X,_} <- L,
                                        {_,Y} <- L,
                                        {Z,_} <- L,
                                        X =:= Y, Y == Z
                                     ]),
                  qlc:e(Q).">>,
           [],
           {warnings,[{3,qlc,too_complex_join}]}},

          {two,
           <<"two() ->
                  Q = qlc:q([{X,Y,Z,W} || 
                      {X} <- [], 
                      {Y} <- [], 
                      {Z} <- [], 
                      {W} <- [], 
                      X =:= Y, 
                      Z =:= W],{join,merge}),
                  qlc:e(Q).">>,
           [],
           {warnings,[{2,qlc,too_many_joins}]}}
       ],

    compile(Config, Ts),

    Ts2 = [{three,
            <<"three() ->
                  L = [],
                  Q = qlc:q([{X,Y,Z} || {X,_} <- L,
                                        {_,Y} <- L,
                                        {Z,_} <- L,
                                        X =:= Y, Y == Z
                                     ]),
                  qlc:e(Q).">>,
            [],
            {[],
             ["cannot handle join of three or more generators efficiently"]}},

          {two,
           <<"two() ->
                  Q = qlc:q([{X,Y,Z,W} || 
                      {X} <- [], 
                      {Y} <- [], 
                      {Z} <- [], 
                      {W} <- [], 
                      X =:= Y, 
                      Z =:= W],{join,merge}),
                  qlc:e(Q).">>,
           [],
           {[],["cannot handle more than one join efficiently"]}}
       ],

    compile_format(Config, Ts2),

    ok.


%% OTP-5644. Handle the new language element M:F/A.
otp_5644(Config) when is_list(Config) ->
    Ts = [
       <<"Q = qlc:q([fun modul:mfa/0 || _ <- [1,2], 
                                        is_function(fun modul:mfa/0, 0)]),
          [_,_] = qlc:eval(Q)">>
       ],
    
    run(Config, Ts),
    ok.

%% OTP-5195. Allow traverse functions returning terms.
otp_5195(Config) when is_list(Config) ->
    %% Several minor improvements have been implemented in OTP-5195.
    %% The test cases are spread all over... except these.
    %%
    %% Traverse functions returning terms.

    Ts = [<<"L = [1,2,3],
             Err = {error,modul,err},
             H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]),
             Err = qlc:e(H)">>,

          <<"Err = {error,modul,err},
             TravFun = fun() -> Err end,
             H1 = qlc:sort(qlc:q([X || X <- qlc:table(TravFun, [])])),
             H = qlc:q([{X} || X <- H1]),
             Err = qlc:e(H)">>,

          <<"L = [1,2,3],
             Err = {error,modul,err},
             H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]),
             C = qlc:cursor(H),
             R = qlc:next_answers(C, all_remaining),
             qlc:delete_cursor(C),
             Err = R">>,

          <<"L = [1,2,3],
             Err = {error,modul,err},
             H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]),
             F = fun(Obj, A) -> A++[Obj] end,
             Err = qlc:fold(F, [], H)">>,

          <<"Err = {error,modul,err},
             TravFun = fun() -> Err end,
             H1 = qlc:sort(qlc:q([X || X <- qlc:table(TravFun, [])])),
             H = qlc:q([{X} || X <- H1]),
             F = fun(Obj, A) -> A++[Obj] end,
             Err = qlc:fold(F, [], H)">>,

          <<"Q1 = qlc:append([qlc:append([ugly()]),[3]]),
             Q = qlc:q([X || X <- Q1]),
             42 = qlc:e(Q),
             ok.

             ugly() ->
                 [apa | fun() -> 42 end].
             foo() -> bar">>,

          <<"L = [1,2,3],
             Err = {error,modul,err},
             H = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]),
             H1 = qlc:q([X || X <- H], unique),
             Err = qlc:e(H1)">>,

          <<"Err = {error, module, err},
             L = [1,2,3],
             H1 = qlc:q([{X} || X <- qlc_SUITE:table_error(L, Err)]),
             H = qlc:q([{X,Y,Z} || X <- H1, Y <- H1, Z <- L], cache),
             qlc:e(H, cache_all)">>,

          <<"Err = {error, module, err},
             L = [1,2,3],
             H1 = qlc:q([X || X <- qlc_SUITE:table_error(L, Err)]),
             H = qlc:q([{X,Y,Z} || X <- H1, Y <- H1, Z <- L], cache),
             qlc:e(H, [cache_all,unique_all])">>,

          <<"L = [{1},{2},{3}],
             H = qlc:q([X || {X} <- qlc_SUITE:table_lookup_error(L), 
                             X =:= 2]),
             {error, lookup, failed} = qlc:e(H)">>,

          %% The traverse function can return any value, but it must not
          %% return an improper list. Improper lists must not be given anyway.
          <<"{'EXIT', {{badfun,a},_}} =
             (catch qlc:e(qlc:q([{X} || X <- [1 | a], begin true end])))">>

       ],
    
    run(Config, Ts),

    Ts2 = [<<"Q = qlc:q([{X,Y} || {X} <- [{1},{2},{3}],
                                  begin
                                      %% Used to generate a badly formed file
                                      Y = 3, true
                                  end,
                                  X =:= Y]),
              [{3,3}] = qlc:e(Q)">>],
    run(Config, Ts2),

    ok.

%% OTP-6038. Bug fixes: unique and keysort; cache.
otp_6038_bug(Config) when is_list(Config) ->
    %% The 'unique' option can no longer be merged with the keysort options.
    %% This used to return [{1,a},{1,c},{2,b},{2,d}], but since 
    %% file_sorter:keysort now removes duplicates based on keys, the
    %% correct return value is [{1,a},{2,b}].
    Ts = [<<"H1 = qlc:q([X || X <- [{1,a},{2,b},{1,c},{2,d}]], unique),
             H2 = qlc:keysort(1, H1, [{unique,true}]),
             [{1,a},{2,b}] = qlc:e(H2)">>],

    run(Config, Ts),
    
    %% Sometimes the cache options did not empty the correct tables.
    CTs = [
       <<"Options = [cache,unique],
          V1 = qlc:q([{X,Y} || X <- [1,2], Y <- [3]], Options),
          V2 = qlc:q([{X,Y} || X <- [a,b], Y <- V1]),
          V3 = qlc:q([{X,Y} || X <- [5,6], Y <- [7]], Options),
          Q = qlc:q([{X,Y} || X <- V2, Y <- V3]),
          R = qlc:e(Q),
          L1 = [{X,Y} || X <- [1,2], Y <- [3]],
          L2 = [{X,Y} || X <- [a,b], Y <- L1],
          L3 = [{X,Y} || X <- [5,6], Y <- [7]],
          L = [{X,Y} || X <- L2, Y <- L3],
          true = R =:= L">>,
       <<"Options = [cache,unique],
          V1 = qlc:q([{X,Y} || X <- [1,2], Y <- [3]], Options),
          V2 = qlc:q([{X,Y} || X <- [a,b], Y <- V1]),
          V3 = qlc:q([{X,Y} || X <- [5,6], Y <- [7]], Options),
          V4 = qlc:q([{X,Y} || X <- V2, Y <- V3], Options),
          Q = qlc:q([{X,Y} || X <- [1,2], Y <- V4]),
          R = qlc:e(Q),
          L1 = [{X,Y} || X <- [1,2], Y <- [3]],
          L2 = [{X,Y} || X <- [a,b], Y <- L1],
          L3 = [{X,Y} || X <- [5,6], Y <- [7]],
          L4 = [{X,Y} || X <- L2, Y <- L3],
          L = [{X,Y} || X <- [1,2], Y <- L4],
          true = R =:= L">>
       ],
    run(Config, CTs),

    ok.

%% OTP-6359. dets:select() never returns the empty list.
otp_6359(Config) when is_list(Config) ->
    dets:start(),
    T = luna,
    Fname = filename(T, Config),

    Ts = [
       [<<"T = luna, Fname = \"">>, Fname, <<"\",
           {ok, _} = dets:open_file(T, [{file,Fname}]),
           Q = qlc:q([F || 
                         F <- dets:table(T), 
                         (F band ((1 bsl 0)) =/= 0), 
                         true]),
           [] = qlc:eval(Q),
           ok = dets:close(T),
           file:delete(\"">>, Fname, <<"\"),
           ok">>]
    ],

    run(Config, Ts),
    ok.

%% OTP-6562. compressed = false (should be []) when sorting before join.
otp_6562(Config) when is_list(Config) ->
    Bug = [
      %% This example uses a file to sort E2 on the second column. It is
      %% not easy to verify that this happens; the file_sorter module's
      %% size option cannot be set in this case. But it is not likely
      %% that the default size (512 KiB) will ever change, so it should
      %% be future safe.
      <<"E1 = create_ets(1, 10),
         E2 = create_ets(5, 150000),
         Q = qlc:q([{XX,YY} ||
                       {X,XX} <- ets:table(E1),
                       {YY,Y} <- ets:table(E2),
                       X == Y],
                   {join,merge}),
         [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}] = qlc:e(Q),
         ets:delete(E1),
         ets:delete(E2)">>
    ],
    run(Config, Bug),

    Bits = [
       {otp_6562_1,
        <<"otp_6562_1() ->
               Q = qlc:q([X || <<X:8>> <= <<\"hej\">>]),
               qlc:info(Q).
        ">>,
        [],
        {errors,[{2,qlc,binary_generator}],
         []}}
       ],
    [] = compile(Config, Bits),

    R1 = {error,qlc,{1,qlc,binary_generator}}
             = qlc:string_to_handle("[X || <<X:8>> <= <<\"hej\">>]."),
    "1: cannot handle binary generators\n" =
             lists:flatten(qlc:format_error(R1)),

    ok.

%% OTP-6590. Bug fix (join info).
otp_6590(Config) when is_list(Config) ->
    Ts = [<<"fun(Tab1Value) -> 
                    Q = qlc:q([T1#tab1.id || T1 <- [#tab1{id = id1,
                                                          value = v, 
                                                          tab2_id = id}],
                                             T2 <- [#tab2{id = id}],
                                             T1#tab1.value =:= Tab1Value,
                                             T1#tab1.tab2_id =:= T2#tab2.id]),
                    [id1] = qlc:e(Q)
            end(v)">>],

    run(Config, <<"-record(tab1, {id, tab2_id, value}).
                         -record(tab2, {id, value}).\n">>, Ts),
    ok.

%% OTP-6673. Optimizations and fixes.
otp_6673(Config) when is_list(Config) ->
    Ts_PT = 
        [<<"etsc(fun(E1) ->
                etsc(fun(E2) ->
                       Q = qlc:q([{A,B,C,D} || 
                                     {A,B} <- ets:table(E1),
                                     {C,D} <- ets:table(E2),
                                     A =:= 2, % lookup
                                     B =:= D, % join
                                     C =:= g]), % lookup invalidated by join
                       {qlc,_,[{generate,_,
                                {qlc,_,
                                 [{generate,_,
                                   {qlc,_,[{generate,_,
                                            {keysort,
                                             {list,{table,_},
                                              [{{'$1','$2'},[],['$_']}]},
                                             2,[]}},_],[]}},
                                  {generate,_,{qlc,_,
                                    [{generate,_,
                                      {keysort,{table,_},2,[]}}],
                                    []}},_],
                                 [{join,merge}]}},_,_],[]} = i(Q),
                       [{2,y,g,y}] = qlc:e(Q)
                     end, [{f,x},{g,y},{h,z}])
                 end, 
                 [{1,x},{2,y},{3,z}])">>,
         <<"etsc(fun(E1) ->
                etsc(fun(E2) ->
                       Q = qlc:q([{A,B,C,D} || 
                                     {A,B} <- ets:table(E1),
                                     {C,D} <- ets:table(E2),
                                     A =:= 2, % lookup
                                     C =:= g, % lookup
                                     B =:= D]), % join
                       {qlc,_,[{generate,_,
                                {qlc,_,
                                 [{generate,_,
                                   {qlc,_,[{generate,_,
                                            {keysort,
                                             {list,{table,_},
                                              [{{'$1','$2'},[],['$_']}]},
                                             2,[]}},_],[]}},
                                  {generate,_,{qlc,_,
                                               [{generate,_,
                                                 {keysort,
                                                  {list,{table,_},
                                                   [{{'$1','$2'},[],['$_']}]},
                                                  2,[]}},_],[]}},_],
                                 [{join,merge}]}},_],[]} = i(Q),
                       [{2,y,g,y}] = qlc:e(Q)
                     end, [{f,x},{g,y},{h,z}])
                 end, 
                 [{1,x},{2,y},{3,z}])">>],

    run(Config, Ts_PT),

    MS = ets:fun2ms(fun({X,_Y}=T) when X > 1 -> T end),
    Ts_RT = [
        [<<"%% Explicit match-spec. ets:table() ensures there is no lookup
            %% function, which means that lookup join will not be considered.
            MS = ">>, io_lib:format("~w", [MS]), <<",
            etsc(fun(E) ->
                         F = fun(J) ->
                                   qlc:q([{X,W} ||
                                             {X,_Y} <- 
                                                 ets:table(E,{traverse,
                                                              {select,MS}}),
                                             {Z,W} <- [{1,1},{2,2},{3,3}],
                                             X =:= Z], {join,J})
                             end,
                         Qm = F(any),
                         [{2,2},{3,3}] = qlc:e(Qm),
                         {'EXIT',{cannot_carry_out_join,_}} = 
                             (catch qlc:e(F(lookup)))
                 end, [{1,a},{2,b},{3,c}])">>],

         <<"%% The filter 'A =< y' can be evaluated by traversing E1 using a
            %% match specification, but then lookup join cannot use E1 for
            %% looking up keys. This example shows that the filter is kept if
            %% lookup join is employed (otherwise it is optimized away since
            %% the match spec is used).
            etsc(fun(E1) ->
                         Q = qlc:q([{A,B,C,D} || 
                                       {A,B} <- ets:table(E1),
                                       {C,D} <- [{x,f},{y,g},{z,h}],
                                       A =< y, % kept
                                       A =:= C], {join,lookup}),
                         [{x,1,x,f},{y,2,y,g}] = lists:sort(qlc:e(Q))
                 end, [{x,1},{y,2},{z,3}])">>

    ],
    run(Config, Ts_RT),

    ok.

%% OTP-6964. New option 'tmpdir_usage'.
otp_6964(Config) when is_list(Config) ->
    T1 = [
       <<"Q1 = qlc:q([{X} || X <- [1,2]]),
          {'EXIT', {badarg,_}} = (catch qlc:e(Q1, {tmpdir_usage,bad})),
          %% merge join
          F = fun(Use) ->
                      L1 = [{Y,a} || Y <- lists:seq(1, 2)],
                      L2 = [{a,Y} || Y <- lists:seq(1, 10000)],
                      Q = qlc:q([{XX,YY} ||
                                    {XX,X} <- L1,
                                    {Y,YY} <- L2,
                                    X == Y],
                                {join,merge}),
                      qlc:e(Q, [{max_list_size,64*1024},{tmpdir_usage,Use}])
              end,
          D = erlang:system_flag(backtrace_depth, 0),
      try
          20000 = length(F(allowed)),
          ErrReply = F(not_allowed),
          {error, qlc, {tmpdir_usage,joining}} = ErrReply,
          \"temporary file was needed for joining\n\" = 
              lists:flatten(qlc:format_error(ErrReply)),
          qlc_SUITE:install_error_logger(),
          20000 = length(F(warning_msg)),
          {warning, joining} = qlc_SUITE:read_error_logger(),
          20000 = length(F(info_msg)),
          {info, joining} = qlc_SUITE:read_error_logger(),
          20000 = length(F(error_msg)),
          {error, joining} = qlc_SUITE:read_error_logger()
      after
          _ = erlang:system_flag(backtrace_depth, D)
      end,
          qlc_SUITE:uninstall_error_logger()">>],
    run(Config, T1),

    T2 = [
       <<"%% File sorter.
          T = lists:seq(1, 10000),
          Q0 = qlc:q([{X} || X <- [T,T,T], begin X > 0 end], 
                     [{cache,list},unique]),
          Q1 = qlc:q([{X,Y,Z} ||
                         X <- Q0,
                         Y <- Q0,
                         Z <- Q0],
                     [{cache,list},unique]),
          Q = qlc:q([{X, Y} || Y <- [1], X <- Q1]),
          F = fun(Use) ->
                      qlc:e(Q, [{max_list_size,10000},{tmpdir_usage,Use}])
              end,
          1 = length(F(allowed)),
          ErrReply = F(not_allowed),
          {error, qlc, {tmpdir_usage,caching}} = ErrReply,
          \"temporary file was needed for caching\n\" = 
              lists:flatten(qlc:format_error(ErrReply)),
          qlc_SUITE:install_error_logger(),
          1 = length(F(error_msg)),
          {error, caching} = qlc_SUITE:read_error_logger(),
          {error, caching} = qlc_SUITE:read_error_logger(),
          1 = length(F(warning_msg)),
          {warning, caching} = qlc_SUITE:read_error_logger(),
          {warning, caching} = qlc_SUITE:read_error_logger(),
          1 = length(F(info_msg)),
          {info, caching} = qlc_SUITE:read_error_logger(),
          {info, caching} = qlc_SUITE:read_error_logger(),
          qlc_SUITE:uninstall_error_logger()">>],

    run(Config, T2),

    T3 = [
       <<"%% sort/keysort
          E1 = create_ets(1, 10),
          E2 = create_ets(5, 50000),
          Q = qlc:q([{XX,YY} ||
                        {X,XX} <- ets:table(E1),
                        {YY,Y} <- ets:table(E2),
                        X == Y],
                    {join,merge}),
          F = fun(Use) ->
                      qlc:e(Q, {tmpdir_usage,Use})
              end,
          ErrReply = F(not_allowed),
          {error,qlc,{tmpdir_usage,sorting}} = ErrReply,
          \"temporary file was needed for sorting\n\" = 
              lists:flatten(qlc:format_error(ErrReply)),
          qlc_SUITE:install_error_logger(),
          L = [{5,5},{6,6},{7,7},{8,8},{9,9},{10,10}],
          L = F(allowed),
          L = F(error_msg),
          {error, sorting} = qlc_SUITE:read_error_logger(),
          L = F(info_msg),
          {info, sorting} = qlc_SUITE:read_error_logger(),
          L = F(warning_msg),
          {warning, sorting} = qlc_SUITE:read_error_logger(),
          qlc_SUITE:uninstall_error_logger(),
          ets:delete(E1),
          ets:delete(E2)">>],
    run(Config, T3),

    T4 = [
       <<"%% cache list
          etsc(fun(E) ->
                       Q0 = qlc:q([X || X <- ets:table(E),
                                        begin element(1, X) > 5 end],
                                  {cache,list}),
                       Q = qlc:q([{X, element(1,Y)} || X <- lists:seq(1, 5),
                                                       Y <- Q0]),
                       R = [{X,Y} || X <- lists:seq(1, 5), 
                                     Y <- lists:seq(6, 10)],
                       F = fun(Use) ->
                                   qlc:e(Q, [{max_list_size, 100*1024},
                                             {tmpdir_usage, Use}])
                           end,
                       R = lists:sort(F(allowed)),
                       qlc_SUITE:install_error_logger(),
                       R = lists:sort(F(info_msg)),
                       {info, caching} = qlc_SUITE:read_error_logger(),
                       R = lists:sort(F(error_msg)),
                       {error, caching} = qlc_SUITE:read_error_logger(),
                       R = lists:sort(F(warning_msg)),
                       {warning, caching} = qlc_SUITE:read_error_logger(),
                       qlc_SUITE:uninstall_error_logger(),
                       ErrReply = F(not_allowed),
                       {error,qlc,{tmpdir_usage,caching}} = ErrReply,
                       \"temporary file was needed for caching\n\" = 
                           lists:flatten(qlc:format_error(ErrReply))
               end, [{keypos,1}], [{I,a,lists:duplicate(100000,1)} || 
                                       I <- lists:seq(1, 10)])">>],
    run(Config, T4),
    ok.

%% OTP-7238. info-option 'depth', &c.
otp_7238(Config) when is_list(Config) ->
    dets:start(),
    T = otp_7238, 
    Fname = filename(T, Config),

    ok = compile_gb_table(Config),

    %% A few more warnings.
    T1 = [
       %% The same error message string, but with different tags
       %% (the strings are not compared :-(
       {nomatch_1, 
        <<"nomatch_1() ->
               {qlc:q([X || X={X} <- []]), [t || \"a\"=\"b\" <- []]}.">>,
        [],
        %% {warnings,[{{2,30},qlc,nomatch_pattern},
        %%            {{2,44},v3_core,nomatch}]}},
        {warnings,[{2,v3_core,nomatch}]}},

       %% Not found by qlc...
       {nomatch_2,
        <<"nomatch_2() ->
               qlc:q([t || {\"a\"++\"b\"} = {\"ac\"} <- []]).">>,
        [],
        {warnings,[{{2,22},v3_core,nomatch}]}},

       {nomatch_3,
        <<"nomatch_3() ->
               qlc:q([t || [$a, $b] = \"ba\" <- []]).">>,
        [],
        %% {warnings,[{{2,37},qlc,nomatch_pattern}]}},
        {warnings,[{2,v3_core,nomatch}]}},

       %% Not found by qlc...
       {nomatch_4,
        <<"nomatch_4() ->
               qlc:q([t || \"a\"++_=\"b\" <- []]).">>,
        [],
        {warnings,[{{2,22},v3_core,nomatch}]}},

       %% Found neither by the compiler nor by qlc...
       {nomatch_5,
        <<"nomatch_5() ->
               qlc:q([X || X = <<X>> <- [3]]).">>,
        [],
        []},

       {nomatch_6,
        <<"nomatch_6() ->
               qlc:q([X || X <- [],
                           X =:= {X}]).">>,
        [],
        %% {warnings,[{{3,30},qlc,nomatch_filter}]}},
        []},

       {nomatch_7,
        <<"nomatch_7() ->
               qlc:q([X || {X=Y,{Y}=X} <- []]).">>,
        [],
        %% {warnings,[{{2,28},qlc,nomatch_pattern}]}},
        []},

       {nomatch_8,
        <<"nomatch_8() ->
               qlc:q([X || {X={},X=[]} <- []]).">>,
        [],
        %% {warnings,[{{2,28},qlc,nomatch_pattern}]}},
        []},

       {nomatch_9,
        <<"nomatch_9() ->
               qlc:q([X || X <- [], X =:= {}, X =:= []]).">>,
        [],
        %% {warnings,[{{2,49},qlc,nomatch_filter}]}},
        []},

       {nomatch_10,
        <<"nomatch_10() ->
               qlc:q([X || X <- [],
                           ((X =:= 1) or (X =:= 2)) and (X =:= 3)]).">>,
        [],
        %% {warnings,[{{3,53},qlc,nomatch_filter}]}},
        []},

       {nomatch_11,
        <<"nomatch_11() ->
               qlc:q([X || X <- [], x =:= []]).">>,
        [],
        %% {warnings,[{{2,39},qlc,nomatch_filter}]}},
        {warnings,[{2,sys_core_fold,nomatch_guard}]}},

       {nomatch_12,
        <<"nomatch_12() ->
               qlc:q([X || X={} <- [], X =:= []]).">>,
        [],
        %% {warnings,[{{2,42},qlc,nomatch_filter}]}},
        []},

       {nomatch_13,
        <<"nomatch_13() ->
               qlc:q([Z || Z <- [], 
                           X={X} <- [], 
                           Y={Y} <- []]).">>,
        [],
        %% {warnings,[{{3,29},qlc,nomatch_pattern},
        %%            {{4,29},qlc,nomatch_pattern}]}},
        []},

       {nomatch_14,
        <<"nomatch_14() ->
               qlc:q([X || X={X} <- [],
                           1 > 0,
                           1 > X]).">>,
        [],
        %% {warnings,[{{2,29},qlc,nomatch_pattern}]}},
        []},

       {nomatch_15,
        <<"nomatch_15() ->
              qlc:q([{X,Y} || X={X} <- [1],
                              Y <- [1],
                              1 > 0,
                              1 > X]).">>,
        [],
        %% {warnings,[{{2,32},qlc,nomatch_pattern}]}},
        []},

       %% Template warning.
       {nomatch_template1,
        <<"nomatch_template1() ->
               qlc:q([{X} = {} || X <- []]).">>,
        [],
        {warnings,[{2,sys_core_fold,no_clause_match}]}}
         ],
    [] = compile(Config, T1),

    %% 'depth' is a new option used by info()
    T2 = [
       %% Firstly: lists
       <<"L = [[a,b,c],{a,b,c},[],<<\"foobar\">>],
          Q = qlc:q([{X} || X <- L]),
          {call, _,
           {remote,_,{atom,_,ets},{atom,_,match_spec_run}},
           [{cons,_,{atom,_,'...'},
             {cons,_,{atom,_,'...'},
              {cons,_,{nil,_},{cons,_,{atom,_,'...'},{nil,_}}}}},
            _]} = qlc:info(Q, [{format,abstract_code},{depth,0}]),

          {call,_,_,
           [{cons,_,{cons,_,{atom,_,'...'},{nil,_}},
             {cons,_,
              {tuple,_,[{atom,_,'...'}]},
              {cons,_,{nil,_},
               {cons,_,
                {bin,_,
                 [{_,_,{_,_,$.},_,_},
                  {_,_,{_,_,$.},_,_},
                  {_,_,{_,_,$.},_,_}]},
                {nil,_}}}}},
            _]} = qlc:info(Q, [{format,abstract_code},{depth,1}]),

          {call,_,
           _,
          [{cons,_,{cons,_,{atom,_,a},{atom,_,'...'}},
            {cons,_,
             {tuple,_,[{atom,_,a},{atom,_,'...'}]},
             {cons,_,{nil,_},
              {cons,_,
               {bin,_,
                [{_,_,{_,_,$f},_,_},
                 {_,_,{_,_,$.},_,_},
                 {_,_,{_,_,$.},_,_},
                 {_,_,{_,_,$.},_,_}]},
               {nil,_}}}}},
          _]} = qlc:info(Q, [{format,abstract_code},{depth,2}]),

          {call,_,_,
           [{cons,_,
             {cons,_,{atom,_,a},{cons,_,{atom,_,b},{atom,_,'...'}}},
             {cons,_,
              {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,'...'}]},
              {cons,_,{nil,_},
               {cons,_,
                {bin,_,
                 [{_,_,{_,_,$f},_,_},
                  {_,_,{_,_,$o},_,_},_,_,_]},
                {nil,_}}}}},
            _]} = qlc:info(Q, [{format,abstract_code},{depth,3}]),

          {call,_,_,
           [{cons,_,
             {cons,_,
              {atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c},{nil,_}}}},
             {cons,_,
              {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,c}]},
              {cons,_,{nil,_},
               {cons,_,
                {bin,_,
                 [{_,_,{_,_,$f},_,_},
                  {_,_,{_,_,$o},_,_},
                  {_,_,{_,_,$o},_,_},
                  {_,_,{_,_,$b},_,_},
                  {_,_,{_,_,$a},_,_},
                  {_,_,{_,_,$r},_,_}]},
                {nil,_}}}}},
            _]} = qlc:info(Q, [{format,abstract_code},{depth,10}]),

          {call,_,_,
           [{cons,_,
             {cons,_,
              {atom,_,a},{cons,_,{atom,_,b},{cons,_,{atom,_,c},{nil,_}}}},
             {cons,_,
              {tuple,_,[{atom,_,a},{atom,_,b},{atom,_,c}]},
              {cons,_,{nil,_},
               {cons,_,
                {bin,_,
                 [{_,_,{_,_,$f},_,_},
                  {_,_,{_,_,$o},_,_},
                  {_,_,{_,_,$o},_,_},
                  {_,_,{_,_,$b},_,_},
                  {_,_,{_,_,$a},_,_},
                  {_,_,{_,_,$r},_,_}]},
                {nil,_}}}}},
            _]} = qlc:info(Q, [{format,abstract_code},{depth,infinity}])">>,
       
       %% Secondly: looked up keys
       <<"F = fun(D) ->
                etsc(fun(E) ->
                       Q = qlc:q([C || {N,C} <- ets:table(E), 
                                       (N =:= {2,2}) or (N =:= {3,3})]),
                       F = qlc:info(Q, [{format,abstract_code},{depth,D}]),
                       {call,_,_,[{call,_,_,[_Fun,Values]},_]} = F,
                       [b,c] = lists:sort(qlc:eval(Q)),
                       Values
                     end, [{{1,1},a},{{2,2},b},{{3,3},c},{{4,4},d}])
              end,

          [{cons,_,{atom,_,'...'},{cons,_,{atom,_,'...'},{nil,_}}},
           {cons,_,
            {tuple,_,[{atom,_,'...'}]},
            {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}},
           {cons,_,
            {tuple,_,[{integer,_,2},{atom,_,'...'}]},
            {cons,_,{tuple,_,[{integer,_,3},{atom,_,'...'}]},{nil,_}}},
           {cons,_,
            {tuple,_,[{integer,_,2},{integer,_,2}]},
            {cons,_,{tuple,_,[{integer,_,3},{integer,_,3}]},{nil,_}}},
           {cons,_,
            {tuple,_,[{integer,_,2},{integer,_,2}]},
            {cons,_,{tuple,_,[{integer,_,3},{integer,_,3}]},{nil,_}}}] =
              lists:map(F, [0,1,2,3,infinity])">>,
       [<<"T = otp_7238, Fname = \"">>, Fname, <<"\",
           {ok, _} = dets:open_file(T, [{file,Fname}]),
           ok = dets:insert(T, [{{1,1},a},{{2,2},b},{{3,3},c},{{4,4},d}]),
           Q = qlc:q([C || {N,C} <- dets:table(T), 
                           (N =:= {2,2}) or (N =:= {3,3})]),
           F = qlc:info(Q, [{format,abstract_code},{depth,1}]),
           [b,c] = lists:sort(qlc:eval(Q)),
           {call,_,_,
            [{call,_,_,
              [_,
               {cons,_,
                {tuple,_,[{atom,_,'...'}]},
                {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}]},
             _]} = F,
           ok = dets:close(T),
           file:delete(\"">>, Fname, <<"\")">>],

       %% Thirdly: format_fun has been extended (in particular: gb_table)
       <<"T = gb_trees:from_orddict([{{1,a},w},{{2,b},v},{{3,c},u}]),
          QH = qlc:q([X || {{X,Y},_} <- gb_table:table(T),
                           ((X =:= 1) or (X =:= 2)),
                           ((Y =:= a) or (Y =:= b) or (Y =:= c))]),
          {call,_,_,
           [{call,_,_,
             [{'fun',_,
               {clauses,
                [{clause,_,_,[],
                  [{'case',_,
                    {call,_,_,
                     [_,
                      {call,_,_,
                       [{cons,_,
                         {tuple,_,[{atom,_,'...'}]},
                         {cons,_,
                          {tuple,_,[{atom,_,'...'}]},
                          {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}}]}]},
                    [_,_]}]}]}},
              {cons,_,
               {tuple,_,[{atom,_,'...'}]},
               {cons,_,
                {tuple,_,[{atom,_,'...'}]},
                {cons,_,
                 {tuple,_,[{atom,_,'...'}]},
                 {cons,_,
                  {tuple,_,[{atom,_,'...'}]},
                  {cons,_,
                   {tuple,_,[{atom,_,'...'}]},
                   {cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}}}}}}]},
            {call,_,_,
             [{cons,_,{tuple,_,[{atom,_,'...'}]},{nil,_}}]}]} = 
            qlc:info(QH, [{format,abstract_code},{depth,1}])">>,
       <<"T1 = [{1,1,a},{2,2,b},{3,3,c},{4,4,d}],
          T2 = [{x,1},{y,1},{z,2}],
          QH1 = T1,
          T = gb_trees:from_orddict(T2),
          QH2 = qlc:q([X || {_,X} <- gb_table:table(T)], cache),
          Q = qlc:q([{X1,X2,X3} || {X1,X2,X3} <- QH1, 
                                   Y2 <- QH2, 
                                   X2 =:= Y2]),
          {block,_,
           [{match,_,_,
             {call,_,_,
              [{lc,_,_,
                [{generate,_,_,
                  {call,_,_,
                   [{call,_,_,
                     [{cons,_,
                       {tuple,_,[{atom,_,'...'}]},
                       {atom,_,'...'}}]}]}}]},
               _]}},
            {call,_,_,
             [{lc,_,_,
               [{generate,_,_,
                 {cons,_,{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}}},
                _,_]}]}]} = 
              qlc:info(Q, [{format,abstract_code},{depth, 1},
                           {n_elements,1}])">>,
       <<"L = [{{key,1},a},{{key,2},b},{{key,3},c}],
          T = gb_trees:from_orddict(orddict:from_list(L)),
          Q = qlc:q([K || {K,_} <- gb_table:table(T), 
                                   (K =:= {key,1}) or (K =:= {key,2})]),
{call,_,_,
 [{call,_,_,
   [{'fun',_,
     {clauses,
      [{clause,_,_,[],
        [{'case',_,
          {call,_,_,
           [_,
            {call,_,_,
             [{cons,_,
               {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]},
               {cons,_,
                {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]},
                {cons,_,
                 {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]},
                 {nil,_}}}}]}]},
          _}]}]}},
    {cons,_,
     {tuple,_,[{atom,_,key},{atom,_,'...'}]},
     {cons,_,{tuple,_,[{atom,_,key},{atom,_,'...'}]},{nil,_}}}]},
  {call,_,
   {remote,_,{atom,_,ets},{atom,_,match_spec_compile}},
   [{cons,_,
     {tuple,_,[{tuple,_,[{atom,_,'...'}]},{atom,_,'...'}]},
     {nil,_}}]}]} = 
          qlc:info(Q, [{format,abstract_code},{depth, 2}])">>

         ],
    run(Config, T2),

    T3 = [
%%        {nomatch_6,
%%         <<"nomatch_6() ->
%%                qlc:q([X || X <- [],
%%                            X =:= {X}]).">>,
%%         [],
%%         {[],["filter evaluates to 'false'"]}},

%%        {nomatch_7,
%%         <<"nomatch_7() ->
%%                qlc:q([X || {X=Y,{Y}=X} <- []]).">>,
%%         [],
%%         {[],["pattern cannot possibly match"]}}
       ],
    compile_format(Config, T3),

    %% *Very* simple test - just check that it doesn't crash.
    Type = [{cres,
             <<"Q = qlc:q([X || {X} <- []]),
                {'EXIT',{{badfun,_},_}} = (catch qlc:e(Q))">>,
             [type_checker],
             []}],
    run(Config, Type),

    ok.
    
%% OTP-7114. Match spec, table and duplicated objects...
otp_7114(Config) when is_list(Config) ->
    Ts = [<<"T = ets:new(t, [bag]),
             [ets:insert(T, {t, I, I div 2}) || I <- lists:seq(1,10)],
             Q1 = qlc:q([element(3, E) || E <- ets:table(T)]),
             [0,1,1,2,2,3,3,4,4,5] = lists:sort(qlc:e(Q1)),
             [0,1,2,3,4,5] = qlc:e(Q1, unique_all),
             [0,1,2,3,4,5] = qlc:e(qlc:sort(Q1), unique_all),
             [0,1,2,3,4,5] = qlc:e(qlc:sort(qlc:e(Q1)), unique_all),
             ets:delete(T),
             ok">>],
    run(Config, Ts).

%% OTP-7232. qlc:info() bug (pids, ports, refs, funs).
otp_7232(Config) when is_list(Config) ->
    Ts = [<<"L = [fun math:sqrt/1, list_to_pid(\"<0.4.1>\"),
                  erlang:make_ref()],
             \"[fun math:sqrt/1, <0.4.1>, #Ref<\" ++ _  = qlc:info(L),
             {call,_,
               {remote,_,{atom,_,qlc},{atom,_,sort}},
               [{cons,_,
                      {'fun',_,{function,{atom,_,math},{atom,_,sqrt},_}},
                      {cons,_,
                            {string,_,\"<0.4.1>\"}, % could use list_to_pid..
                            {cons,_,{string,_,\"#Ref<\"++_},{nil,_}}}},
                {nil,_}]} = 
              qlc:info(qlc:sort(L),{format,abstract_code})">>,

          <<"Q1 = qlc:q([X || X <- [55296,56296]]),
             Q = qlc:sort(Q1, {order, fun(A,B)-> A>B end}),
             \"qlc:sort([55296,56296],[{order,fun'-function/0-fun-2-'/2}])\" =
                format_info(Q, true),
             AC = qlc:info(Q, {format, abstract_code}),
             \"qlc:sort([55296, 56296], [{order, fun '-function/0-fun-2-'/2}])\" =
                binary_to_list(iolist_to_binary(erl_pp:expr(AC)))">>,

         %% OTP-7234. erl_parse:abstract() handles bit strings
          <<"Q = qlc:sort([<<17:9>>]),
             \"[<<8,1:1>>]\" = qlc:info(Q)">>

         ],
    run(Config, Ts).

%% OTP-7552. Merge join bug.
otp_7552(Config) when is_list(Config) ->
    %% The poor performance cannot be observed unless the 
    %% (redundant) join filter is skipped. 
    Ts = [<<"Few = lists:seq(1, 2),
             Many = lists:seq(1, 10),
             S = [d,e],
             L1 = [{Y,a} || Y <- Few] ++ [{'1b',b},{2,b}] ++ 
                    [{Y,X} || X <- S, Y <- Few],
             L2 = [{a,Y} || Y <- Many] ++ 
                    [{b,'1b'}] ++ [{c,1}] ++ 
                    [{X,Y} || X <- S, Y <- Many],
                   F = fun(J) ->
                               qlc:q([{XX,YY} ||
                                         {XX,X} <- L1,
                                         {Y,YY} <- L2,
                                         X == Y],
                                     {join,J})
                       end,
                   Qm = F(merge),
                   Qn = F(nested_loop),
                   true = lists:sort(qlc:e(Qm, {max_list_size,20})) =:= 
                          lists:sort(qlc:e(Qn))">>],
    run(Config, Ts).

%% OTP-7714. Merge join bug.
otp_7714(Config) when is_list(Config) ->
    %% The original example uses Mnesia. This one does not.
    Ts = [<<"E1 = ets:new(set,[]),
             true = ets:insert(E1, {a,1}),
             E2 = ets:new(set,[]),
             _ = [true = ets:insert(E2, {I, 1}) ||
                     I <- lists:seq(1, 3)],
             Q = qlc:q([{A,B} || 
                           {A,I1} <- ets:table(E1),
                           {B,I2} <- ets:table(E2),
                           I1 =:= I2],{join,merge}),
             [{a,1},{a,2},{a,3}] = lists:sort(qlc:e(Q)),
             ets:delete(E1),
             ets:delete(E2)">>],
    run(Config, Ts).

%% OTP-11758. Bug.
otp_11758(Config) when is_list(Config) ->
    Ts = [<<"T = ets:new(r, [{keypos, 2}]),
             L = [{rrr, xxx, aaa}, {rrr, yyy, bbb}],
             true = ets:insert(T, L),
             QH = qlc:q([{rrr, B, C} || {rrr, B, C} <- ets:table(T),
                              (B =:= xxx) or (B =:= yyy) and (C =:= aaa)]),
             [{rrr,xxx,aaa}] = qlc:e(QH),
             ets:delete(T)">>],
    run(Config, Ts).

%% OTP-6674. match/comparison.
otp_6674(Config) when is_list(Config) ->

    ok = compile_gb_table(Config),

    Ts = [%% lookup join
          <<"E = ets:new(join, [ordered_set]),
             true = ets:insert(E, [{1,a},{2,b},{3,c}]),
             Q = qlc:q([{X, Y} || {X,_} <- ets:table(E),
                                  {Y} <- [{0},{1},{2}],
                                  X == Y]),
             {0,1,0,0} = join_info(Q),
             [{1,1},{2,2}] = qlc:e(Q),
             ets:delete(E)">>,

          <<"E = ets:new(join, [ordered_set]),
             true = ets:insert(E, [{1,a},{2,b},{3,c}]),
             Q = qlc:q([{X, Y} || {X,_} <- ets:table(E),
                                  {Y} <- [{0},{1},{2}],
                                  X =:= Y]),
             {0,1,0,0} = join_info(Q),
             {block,_,
              [_,
               {match,_,_,
                 {call,_,_,
                  [{lc,_,_,
                    [_,_,{op,_,'==',_,_}]},
                   {cons,_,
                    {tuple,_,[{atom,_,join},{atom,_,lookup}]},_}]}},
               _]} = qlc:info(Q, {format, abstract_code}),
             [{1,1},{2,2}] = qlc:e(Q),
             ets:delete(E)">>,

       <<"E = ets:new(join, [set]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E),
                               {Y} <- [{0},{1},{2}],
                               X == Y], {join, lookup}),
          {'EXIT',{cannot_carry_out_join,_}} = (catch qlc:e(Q)),
          ets:delete(E)">>,

       %% Lookup join possible in both directions.
       <<"E1 = ets:new(join, [ordered_set]),
          E2 = ets:new(join, [set]),
          true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]),
          true = ets:insert(E2, [{0},{1},{2}]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1),
                               {Y} <- ets:table(E2),
                               X == Y],{join,lookup}), % skipped
          {qlc,_,
            [{generate,_,
                 {qlc,_,
                     [{generate,_,
                          {qlc,_,[{generate,_,{table,{ets,table,[_]}}}],[]}},
                      {generate,_,{table,{ets,table,[_]}}},
                      _],
                     [{join,lookup}]}}],
            []} = qlc:info(Q, {format,debug}),
          {0,1,0,0} = join_info(Q),
          [{1.0,1},{2,2}] = lists:sort(qlc:e(Q)),
          ets:delete(E1), 
          ets:delete(E2)">>,
       <<"E1 = ets:new(join, [ordered_set]),
          E2 = ets:new(join, [set]),
          true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]),
          true = ets:insert(E2, [{0},{1},{2}]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1),
                               {Y} <- ets:table(E2),
                               X =:= Y],{join,merge}), % not skipped
          {1,0,0,1} = join_info(Q),
          [{2,2}] = qlc:e(Q),
          ets:delete(E1), 
          ets:delete(E2)">>,
       <<"E1 = ets:new(join, [ordered_set]),
          E2 = ets:new(join, [set]),
          true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]),
          true = ets:insert(E2, [{0},{1},{2}]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1),
                               {Y} <- ets:table(E2),
                               X =:= Y],{join,lookup}), % skipped
          {qlc,_,
           [{generate,_,
             {qlc,_,
              [{generate,_,
                {qlc,_,
                 [{generate,_,{table,{ets,table,[_]}}}],
                 []}},
               {generate,_,{table,{ets,table,[_]}}},
               _],
              [{join,lookup}]}}],
            []} = qlc:info(Q, {format,debug}),
          {0,1,0,0} = join_info(Q),
          [{2,2}] = qlc:e(Q),
          ets:delete(E1), 
          ets:delete(E2)">>,
       <<"E1 = ets:new(join, [ordered_set]),
          E2 = ets:new(join, [set]),
          true = ets:insert(E1, [{1.0,a},{2,b},{3,c}]),
          true = ets:insert(E2, [{0},{1},{2}]),
          Q = qlc:q([{X, Y} || {X,_} <- ets:table(E1),
                               {Y} <- ets:table(E2),
                               %% Independent of term comparison:
                               X =:= Y, X == Y]),
          {0,1,0,0} = join_info(Q),
          [{2,2}] = qlc:e(Q),
          ets:delete(E1), 
          ets:delete(E2)">>,

       <<"E = ets:new(join, [ordered_set]),
          true = ets:insert(E, [{1,1},{2,2},{3,c}]),
          Q = qlc:q([{X, Y} || {X,Z} <- ets:table(E),
                               {Y} <- [{0},{1},{2}],
                               X == Z, Y == Z]), % cannot skip (yet)
          {qlc,_,
            [{generate,_,
                 {qlc,_,[_,_,_],[{join,lookup}]}},
             _,_],[]} = qlc:info(Q,{format,debug}),
          {0,1,0,0} = join_info(Q),
          [{1,1},{2,2}] = qlc:e(Q),
          ets:delete(E)">>,

       %% The following moved here from skip_filters. It was buggy!
       <<"etsc(fun(E) ->
                 A = 3,
                 Q = qlc:q([X || X <- ets:table(E),
                                 A == element(1,X)]),
                 {table, _} = i(Q),
                 case qlc:e(Q) of
                       [{3},{3.0}] -> ok;
                       [{3.0},{3}] -> ok
                 end,
                 false = lookup_keys(Q)
         end, [{3},{3.0},{c}])">>,
    <<"H1 = qlc:sort([{{192,192.0},1,a},{{192.0,192.0},2,b},{{192,192.0},3,c}]),
       Q = qlc:q([{X, Y} || {{A,B},X,_} <- H1, % does not need keysort(3)
                            A == 192, B =:= 192.0,
                            {Y} <- [{0},{1},{2}],
                            X == Y]),
       A0 = erl_anno:new(0),
       {block,A0,
         [{match,_,_,
           {call,_,_,
            [{lc,_,_,
              [_,
               %% Has to compare extra constant:
               {op,_,'==',
                {tuple,_,[{integer,_,192},{float,_,192.0}]},
                {call,_,{atom,_,element},[{integer,_,1},{var,_,'P0'}]}}]}]}},
          _,_,
          {call,_,_,
           [{lc,_,_,
             [_,
              %% The join filter has been skipped.
              {op,_,'==',{var,_,'A'},{integer,_,192}},
              {op,_,'=:=',{var,_,'B'},{float,_,192.0}}]}]}]}
      = qlc:info(Q, {format,abstract_code}),
       {1,0,0,1} = join_info(Q),
       [{1,1},{2,2}] = qlc:e(Q)">>,

    %% Does not handle more than one lookup value (conjunctive).
    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                                X =:= 1 andalso X == 1.0]),
       false = lookup_keys(H),
       [1] = qlc:e(H)">>,

    %% EqualConstants...
    <<"etsc(fun(E) ->
                Q = qlc:q([{X,Y} || {X} <- ets:table(E), 
                                {Y} <- [{{1}},{{2}},{{1.0}},{{2.0}}],
                                X =:= {1}, X == {1.0},
                                X == Y], {join, merge}),
                [{{1},{1}},{{1},{1.0}}] = lists:sort(qlc:e(Q)),
                false = lookup_keys(Q)
         end, [{{1}}, {{2}}])">>,

    <<"T = gb_trees:from_orddict([{foo,{1}}, {bar,{2}}]),
       Q = qlc:q([{X,Y} || {_,X} <- gb_table:table(T), 
                       {Y} <- [{{1}},{{2}},{{1.0}},{{2.0}}],
                         (X =:= {1}) or (X == {2}), 
                         (X == {1.0}) or (X =:= {2.0}),
                       X == Y], {join, merge}),
       [{{1},{1}},{{1},{1.0}}] = qlc:e(Q)">>,

    %% Compare key
    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X == 1]),
       [1] = lookup_keys(H),
       [1] = qlc:e(H)">>,
    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X == 1.0]),
       [1.0] = lookup_keys(H), % this is how gb_table works...
       [1.0] = qlc:e(H)">>,
    <<"etsc(fun(E) ->
                   H = qlc:q([X || {X,_} <- ets:table(E), 
                                   X == 1.0]),
                   [1] = qlc:e(H), % and this is how ETS works.
                   [1.0] = lookup_keys(H)
           end, [ordered_set], [{1,a},{2,b}])">>,

    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X =:= 2]),
       [2] = lookup_keys(H),
       %% Cannot (generally) remove the matching filter (the table
       %% compares the key). But note that gb_table returns the given
       %% term as key, so in this case the filter _could_ have been removed.
       %% However, there is no callback to inform qlc about that.
       {call,_,_,
             [_,{call,_,_,
               [{cons,_,{tuple,_,
                  [_,{cons,_,
                    {tuple,_,[{atom,_,'=:='},{atom,_,'$1'},{integer,_,2}]},
                    _},_]},_}]}]} =  qlc:info(H, {format,abstract_code}),
       [2] = qlc:e(H)">>,
    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X =:= 2.0]),
       %% Just shows that the term (not the key) is returned.
       [2.0] = lookup_keys(H),
       [2.0] = qlc:e(H)">>,

    <<"I = 1,
       T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X == I]), % imported variable
       [1] = lookup_keys(H),
       {call,_,_,
             [_,{call,_,_,
               [{cons,_,
                 {tuple,_,
                  [{tuple,_,[{atom,_,'$1'},{atom,_,'_'}]},
                   {nil,_}, % the filter has been skipped
                   {cons,_,{atom,_,'$1'},_}]},
                 _}]}]} = qlc:info(H, {format, abstract_code}),
       [1] = qlc:e(H)">>,
    <<"I = 2,
       T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,_} <- gb_table:table(T),
                       X =:= I]),
       [2] = lookup_keys(H),
       {call,_,_,
            [_,{call,_,_,
              [{cons,_,{tuple,_,
                 [_,{cons,_,
                   {tuple,_,
                    [{atom,_,'=:='},
                     {atom,_,'$1'},
                     {tuple,_,[{atom,_,const},{integer,_,2}]}]},
                   _},_]},
                _}]}]} = qlc:info(H, {format, abstract_code}),
          [2] = qlc:e(H)">>,

    <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X,_} <- ets:table(E),
                                 X =:= a]), % skipped
                 [a] = qlc:e(Q),
                 {list,{table,_},_} = i(Q),
                 [a] = lookup_keys(Q)
            end, [ordered_set], [{a,1},{b,2},{3,c}])">>,

    %% Does not find that if for instance X =:= {1} then the filter
    %% X == {1} can be removed.
    <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X =:= {1}, X == {1.0}]),
                [{1}] = qlc:e(Q),
                [{1}] = lookup_keys(Q)
         end, [{{1}}, {{2}}])">>,
    <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X =:= {1}, X == {1.0}]),
                [{1}] = qlc:e(Q),
                false = lookup_keys(Q)
         end, [ordered_set], [{{1}}, {{2}}])">>,
    <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X == {1.0}, X =:= {1}]),
                [{1}] = qlc:e(Q),
                [{1}] = lookup_keys(Q)
         end, [{{1}}, {{2}}])">>,
    <<"etsc(fun(E) ->
                Q = qlc:q([X || {X} <- ets:table(E), 
                                X == {1.0}, X =:= {1}]),
                [{1}] = qlc:e(Q),
                false = lookup_keys(Q)
         end, [ordered_set], [{{1}}, {{2}}])">>,

    <<"E = ets:new(apa, []),
       true = ets:insert(E, [{1,a},{2,b}]),
       {'EXIT', {badarg, _}} = 
              (catch qlc_SUITE:bad_table_key_equality(E)),
       ets:delete(E)">>,

    <<"etsc(fun(E) ->
           Q = qlc:q([X || {X} <- ets:table(E), 
                               X =:= 1, X =:= is_integer(X)]),
           [] = qlc:e(Q),
           [1] = lookup_keys(Q)
       end, [{1}, {2}])">>,

    <<"etsc(fun(E) ->
                 Q = qlc:q([X || {X=1} <- ets:table(E), 
                                 X =:= is_integer(X)]),
                 {call,_,_,
                  [{lc,_,_,
                    [_,
                     {op,_,'=:=',
                      {var,_,'X'},
                      {call,_,
                       {atom,_,is_integer},
                       [{var,_,'X'}]}}]}]} = 
             qlc:info(Q, {format, abstract_code}),
             [] = qlc:e(Q),
             [1] = lookup_keys(Q)
         end, [{1}, {2}])">>,

    <<"T = gb_trees:from_orddict([{1,a},{2,b}]),
       H = qlc:q([X || {X,Y} <- gb_table:table(T),
                                Y =:= a, true, X =:= 1]),
       [1] = lookup_keys(H),
       [1] = qlc:e(H)">>,

    <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]),
       H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T),
                       B == 1]), % skipped
       [{1.0, 1}] = qlc:e(H),
       {qlc,_,[{generate,_,{table,_}}], []} = qlc:info(H, {format,debug})">>,
    <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]),
       H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T),
                    B == 1.0]), % skipped
       [{1.0, 1.0}] = qlc:e(H), % this is how gb_table works...
       {qlc,_,[{generate,_,{table,_}}], []} = qlc:info(H, {format,debug})">>,
    <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]),
       H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T),
                       B =:= 1.0]), % not skipped
       [{1.0, 1.0}] = qlc:e(H),
       {qlc,_,[{generate,_,{table,_}},_], []} = qlc:info(H,{format,debug})">>,
    <<"T = gb_trees:from_orddict([{{1.0,1},a},{{2.0,2},b}]),
       H = qlc:q([X || {{1.0,B}=X,_} <- gb_table:table(T),
                       B =:= 1]), % not skipped
       [{1.0, 1}] = qlc:e(H),
       {qlc,_,[{generate,_,{table,_}},_], []} = qlc:info(H,{format,debug})">>,

    <<"%% The imported variables do not interfere with join.
       E = ets:new(join, [ordered_set]),
       {A, B} = {1,1},
       true = ets:insert(E, [{1,a},{2,b},{3,c}]),
       Q = qlc:q([{X, Y} || {X,_Z} <- ets:table(E),
                            {Y} <- [{0},{1},{2}],
                            X =:= A, Y =:= B, 
                            Y == X], % skipped
                {join, merge}),
       [{1,1}] = qlc:e(Q),
       {qlc,_,
           [{generate,_,
             {qlc,_,
              [{generate,_,
                {qlc,_,[{generate,_,{list,{table,_},_}},_],[]}},
               {generate,_,
                {qlc,_,[{generate,_,{list,_,_}},_],[]}},
               _],
              [{join,merge}]}}],
           []} = qlc:info(Q, {format, debug}),
       ets:delete(E)">>,

    <<"% An old bug: usort() should not be used when matching values
       etsc(fun(E) ->
                   I = 1,
                   H = qlc:q([X || {X,_} <- ets:table(E), 
                                   X =:= 1.0 orelse X =:= I]),
                   [1] = qlc:e(H),
                   [1.0] = lookup_keys(H) % do not look up twice
            end, [set], [{1,a},{2,b}])">>,
    <<"etsc(fun(E) ->
                   H = qlc:q([X || {X,_} <- ets:table(E), 
                                    X =:= 1.0 orelse X == 1]),
                   [1] = qlc:e(H),
                   false = lookup_keys(H) % doesn't handle this case
         end, [ordered_set], [{1,a},{2,b}])">>,

    <<"etsc(fun(E) ->
                 I1 = 1, I2 = 1,
                 H = qlc:q([X || {X,_} <- ets:table(E), 
                                 X =:= I1 orelse X == I2]),
                 [1] = qlc:e(H), % do not look up twice
                 [1] = lookup_keys(H)
         end, [ordered_set], [{1,a},{2,b}])">>,

    <<"etsc(fun(E) ->
                 I1 = 1, I2 = 1, I3 = 1,
                 H = qlc:q([X || {X,_} <- ets:table(E), 
                                 I1 == I2, I1 =:= I3, I3 == I2, I2 =:= I3,
                                 X =:= I1 orelse X == I2
                                 ]),
                 [1] = qlc:e(H),
                 [1] = lookup_keys(H)
         end, [ordered_set], [{1,a},{2,b}])">>,

    <<"E = ets:new(join, [ordered_set]),
       true = ets:insert(E, [{1,a},{2,b,x},{3,c}]),
       Q = qlc:q([P || P <- ets:table(E),
                       P =:= {1,a} orelse P =:= {2,b,x}]),
       [{1,a},{2,b,x}] = qlc:e(Q),
       ets:delete(E)">>,

    <<"etsc(fun(E) ->
                   Q = qlc:q([X || {X,Y} <- ets:table(E), 
                                   ((X =:= 3) or (Y =:= 4))  and (X == a)]),
                   {list,{table,_},_} = i(Q),
                   [] = qlc:e(Q), % a is not an answer
                   [a] = lookup_keys(Q)
          end, [{keypos,1},ordered_set], [{a,3},{b,4}])">>,

    <<"Q = qlc:q([{X,Y} ||
                  {X} <- [{<<3:4>>}],
                  {Y} <- [{<<3:4>>}],
                  X =:= <<1:3,1:1>>,        % <<3:4>>
                  Y =:= <<0:2,1:1,1:1>>,    % <<3:4>>
                  X =:= Y]),
                  [{<<3:4>>,<<3:4>>}] = qlc:e(Q)">>


    ],

    run(Config, Ts).

%% Syntax error.
otp_12946(Config) when is_list(Config) ->
    Text =
        <<"-export([init/0]).
           init() ->
               ok.
           y">>,
    {errors,[{4,erl_parse,_}],[]} = compile_file(Config, Text, []),
    ok.

%% Examples from qlc(3).
manpage(Config) when is_list(Config) ->
    dets:start(),
    ok = compile_gb_table(Config),

    Ts = [
       <<"QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),
          QC = qlc:cursor(QH),
          [{a,1}] = qlc:next_answers(QC, 1),
          [{a,2}] = qlc:next_answers(QC, 1),
          [{b,1},{b,2}] = qlc:next_answers(QC, all_remaining),
          ok = qlc:delete_cursor(QC)">>,

       <<"QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),
          [{a,1},{a,2},{b,1},{b,2}] = qlc:eval(QH)">>,

       <<"QH = [1,2,3,4,5,6],
          21 = qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH)">>,

       <<"QH = qlc:q([{X,Y} || X <- [x,y], Y <- [a,b]]),
          B = \"begin\n\"
              \"    V1 =\n\"
              \"        qlc:q([ \n\"
              \"               SQV ||\n\"
              \"                   SQV <- [x, y]\n\"
              \"              ],\n\"
              \"              [{unique, true}]),\n\"
              \"    V2 =\n\"
              \"        qlc:q([ \n\"
              \"               SQV ||\n\"
              \"                   SQV <- [a, b]\n\"
              \"              ],\n\"
              \"              [{unique, true}]),\n\"
              \"    qlc:q([ \n\"
              \"           {X, Y} ||\n\"
              \"               X <- V1,\n\"
              \"               Y <- V2\n\"
              \"          ],\n\"
              \"          [{unique, true}])\n\"
              \"end\",
          true = B =:= qlc:info(QH, unique_all)">>,

       <<"E1 = ets:new(e1, []),
          E2 = ets:new(e2, []),
          true = ets:insert(E1, [{1,a},{2,b}]),
          true = ets:insert(E2, [{a,1},{b,2}]),
          Q = qlc:q([{X,Z,W} ||
                        {X, Z} <- ets:table(E1),
                        {W, Y} <- ets:table(E2),
                        X =:= Y]),
          L = \"begin\n\"
              \"    V1 =\n\"
              \"        qlc:q([ \n\"
              \"               P0 ||\n\"
              \"                   P0 = {W, Y} <- ets:table(_)\n\"
              \"              ]),\n\"
              \"    V2 =\n\"
              \"        qlc:q([ \n\"
              \"               [G1 | G2] ||\n\"
              \"                   G2 <- V1,\n\"
              \"                   G1 <- ets:table(_),\n\"
              \"                   element(2, G1) =:= element(1, G2)\n\"
              \"              ],\n\"
              \"              [{join, lookup}]),\n\"
              \"    qlc:q([ \n\"
              \"           {X, Z, W} ||\n\"
              \"               [{X, Z} | {W, Y}] <- V2\n\"
              \"          ])\n\"
              \"end\",
          Info1 =
             re:replace(qlc:info(Q), 
                        \"table\\\\(#Ref<[\\.0-9]*>\",
                        \"table(_\", [{return,list},global]),
          F = fun(C) -> C =/= $\n andalso C =/= $\s end,
          Info = lists:filter(F, Info1),
          L1 = lists:filter(F, L),
          L1 = Info,
          ets:delete(E1),
          ets:delete(E2)">>,

       <<"Q = qlc:q([{A,X,Z,W} ||
                        A <- [a,b,c],
                        {X,Z} <- [{a,1},{b,4},{c,6}],
                        {W,Y} <- [{2,a},{3,b},{4,c}],
                        X =:= Y],
                    {cache, list}),
          L =
       \"begin\n\"
       \"    V1 =\n\"
       \"        qlc:q([ \n\"
       \"               P0 ||\n\"
       \"                   P0 = {X, Z} <-\n\"
       \"                       qlc:keysort(1, [{a, 1}, {b, 4}, {c, 6}], [])\n\"
       \"              ]),\n\"
       \"    V2 =\n\"
       \"        qlc:q([ \n\"
       \"               P0 ||\n\"
       \"                   P0 = {W, Y} <-\n\"
       \"                       qlc:keysort(2, [{2, a}, {3, b}, {4, c}], [])\n\"

       \"              ]),\n\"
       \"    V3 =\n\"
       \"        qlc:q([ \n\"
       \"               [G1 | G2] ||\n\"
       \"                   G1 <- V1,\n\"
       \"                   G2 <- V2,\n\"
       \"                   element(1, G1) == element(2, G2)\n\"
       \"              ],\n\"
       \"              [{join, merge}, {cache, list}]),\n\"
       \"    qlc:q([ \n\"
       \"           {A, X, Z, W} ||\n\"
       \"               A <- [a, b, c],\n\"
       \"               [{X, Z} | {W, Y}] <- V3,\n\"
       \"               X =:= Y\n\"
       \"          ])\n\"
       \"end\",
          L = qlc:info(Q)">>,

       <<"E1 = ets:new(t, [set]), % uses =:=/2
          Q1 = qlc:q([K ||
          {K} <- ets:table(E1),
          K == 2.71 orelse K == a]),
          {list,{table,_}, [{{'$1'},[],['$1']}]} = i(Q1),
          true = ets:delete(E1)">>,

       <<"F = fun(E, I) ->
                    qlc:q([V || {K,V} <- ets:table(E), K == I])
              end,
          E2 = ets:new(t, [set]),
          true = ets:insert(E2, [{{2,2},a},{{2,2.0},b},{{2.0,2},c}]),
          Q2 = F(E2, {2,2}),
          {table,{ets,table,[_,
                [{traverse,{select,[{{'$1','$2'},
                                     [{'==','$1',{const,{2,2}}}],
                                     ['$2']}]}}]]}} = i(Q2),
          [a,b,c] = lists:sort(qlc:e(Q2)),
          true = ets:delete(E2),

          E3 = ets:new(t, [ordered_set]), % uses ==/2
          true = ets:insert(E3, [{{2,2.0},b}]),
          Q3 = F(E3,{2,2}),
          {list,{table,_},[{{'$1','$2'},[],['$2']}]} = i(Q3),
          [b] = qlc:e(Q3),
          true = ets:delete(E3)">>,

       <<"T = gb_trees:empty(),
          QH = qlc:q([X || {{X,Y},_} <- gb_table:table(T),
                           ((X == 1) or (X == 2)) andalso
                           ((Y == a) or (Y == b) or (Y == c))]),
          L = \"ets:match_spec_run(lists:flatmap(fun(K) ->
                                        case
                                            gb_trees:lookup(K,
                                                            gb_trees:from_orddict([]))
                                        of
                                            {value, V} ->
                                                [{K, V}];
                                            none ->
                                                []
                                        end
                                 end,
                                 [{1, a},
                                  {1, b},
                                  {1, c},
                                  {2, a},
                                  {2, b},
                                  {2, c}]),
                   ets:match_spec_compile([{{{'$1', '$2'}, '_'},
                                            [],
                                            ['$1']}]))\",
          L = qlc:info(QH)">>
      ],
    run(Config, Ts),

    L = [1,2,3],
    Bs = erl_eval:add_binding('L', L, erl_eval:new_bindings()),
    QH = qlc:string_to_handle("[X+1 || X <- L].", [], Bs),
    [2,3,4] = qlc:eval(QH),

    %% ets(3)
    MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y} end),
    ETs = [
        [<<"true = ets:insert(Tab = ets:new(t, []),[{1,a},{2,b},{3,c},{4,d}]),
            MS = ">>, io_lib:format("~w", [MS]), <<",
            QH1 = ets:table(Tab, [{traverse, {select, MS}}]),

            QH2 = qlc:q([{Y} || {X,Y} <- ets:table(Tab), (X > 1) or (X < 5)]),

            true = qlc:info(QH1) =:= qlc:info(QH2),
            true = ets:delete(Tab)">>]],
    run(Config, ETs),

    %% dets(3)
    DTs = [
        [<<"{ok, T} = dets:open_file(t, []),
            ok = dets:insert(T, [{1,a},{2,b},{3,c},{4,d}]),
            MS = ">>, io_lib:format("~w", [MS]), <<",
            QH1 = dets:table(T, [{traverse, {select, MS}}]),

            QH2 = qlc:q([{Y} || {X,Y} <- dets:table(t), (X > 1) or (X < 5)]),

            true = qlc:info(QH1) =:= qlc:info(QH2),
            ok = dets:close(T)">>]],
    run(Config, DTs),

    ok.

compile_gb_table(Config) ->
    GB_table_file = filename("gb_table.erl", Config),
    ok = file:write_file(GB_table_file, gb_table()),
    {ok, gb_table} = compile:file(GB_table_file, [{outdir,?privdir}]),
    code:purge(gb_table),
    {module, gb_table} =
        code:load_abs(filename:rootname(GB_table_file)),
    ok.

gb_table() ->
    <<"
-module(gb_table).

-export([table/1]).

table(T) ->
    TF = fun() -> qlc_next(gb_trees:next(gb_trees:iterator(T))) end,
    InfoFun = fun(num_of_objects) -> gb_trees:size(T);
                 (keypos) -> 1;
                 (is_sorted_key) -> true;
                 (is_unique_objects) -> true;
                 (_) -> undefined
              end,
    LookupFun =
        fun(1, Ks) ->
                lists:flatmap(fun(K) ->
                                      case gb_trees:lookup(K, T) of
                                          {value, V} -> [{K,V}];
                                          none -> []
                                      end
                              end, Ks)
        end,
    FormatFun =
        fun({all, NElements, ElementFun}) ->
                ValsS = io_lib:format(\"gb_trees:from_orddict(~w)\",
                                      [gb_nodes(T, NElements, ElementFun)]),
                io_lib:format(\"gb_table:table(~s)\", [ValsS]);
           ({lookup, 1, KeyValues, _NElements, ElementFun}) ->
                ValsS = io_lib:format(\"gb_trees:from_orddict(~w)\",
                                      [gb_nodes(T, infinity, ElementFun)]),
                io_lib:format(\"lists:flatmap(fun(K) -> \"
                              \"case gb_trees:lookup(K, ~s) of \"
                              \"{value, V} -> [{K,V}];none -> [] end \"
                              \"end, ~w)\",
                              [ValsS, [ElementFun(KV) || KV <- KeyValues]])
        end,
    qlc:table(TF, [{info_fun, InfoFun}, {format_fun, FormatFun},
                   {lookup_fun, LookupFun},{key_equality,'=='}]).

qlc_next({X, V, S}) ->
    [{X,V} | fun() -> qlc_next(gb_trees:next(S)) end];
qlc_next(none) ->
    [].

gb_nodes(T, infinity, ElementFun) ->
    gb_nodes(T, -1, ElementFun);
gb_nodes(T, NElements, ElementFun) ->
    gb_iter(gb_trees:iterator(T), NElements, ElementFun).

gb_iter(_I, 0, _EFun) ->
    '...';
gb_iter(I0, N, EFun) ->
    case gb_trees:next(I0) of
        {X, V, I} ->
            [EFun({X,V}) | gb_iter(I, N-1, EFun)];
        none ->
            []
    end.
    ">>.


%% OTP-6674. Join info and extra constants.
backward(Config) when is_list(Config) ->
    try_old_join_info(Config),
    ok.

try_old_join_info(Config) ->
    %% Check join info for handlers of extra constants.
    File = filename:join(?datadir, "join_info_compat.erl"),
    M = join_info_compat,
    {ok, M} = compile:file(File, [{outdir, ?datadir}]),
    {module, M} = code:load_abs(filename:rootname(File)),
    H = M:create_handle(),
    A0 = erl_anno:new(0),
    {block,A0,
     [{match,_,_,
       {call,_,_,
        [{lc,_,_,
          [_,
           {op,_,'=:=',
            {float,_,192.0},
            {call,_,{atom,_,element},[{integer,_,1},_]}}]}]}},
      _,_,
      {call,_,_,
       [{lc,_,_,
         [_,
          {op,_,'=:=',{var,_,'B'},{float,_,192.0}},
          {op,_,'==',{var,_,'X'},{var,_,'Y'}}]}]}]}
        = qlc:info(H,{format,abstract_code}),
    [{1,1},{2,2}] = qlc:e(H),

    H2 = M:lookup_handle(),
    {qlc,_,[{generate,_,{qlc,_,_,[{join,lookup}]}},_],[]} =
        qlc:info(H2, {format,debug}),
    [{1,1},{2,2}] = qlc:e(H2).

forward(Config) when is_list(Config) ->
    Ts = [
      %% LC_fun() returns something unknown.
      <<"FakeH = {qlc_handle,{qlc_lc,fun() -> {foo,bar} end,
                             {qlc_opt,false,false,-1,any,[],any,524288}}},
         {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">>,

%% 'f1' should be used for new stuff that does not interfer with old behavior
%%       %% The unused element 'f1' of #qlc_table seems to be used.
%%       <<"DF = fun() -> foo end,
%%          FakeH = {qlc_handle,{qlc_table,DF,
%%                        true,DF,DF,DF,DF,DF,
%%                        undefined,not_undefined,undefined,no_match_spec}},
%%          {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">>,

      %% #qlc_opt has changed.
      <<"H = qlc:q([X || X <- []]),
         {qlc_handle, {qlc_lc, Fun, _Opt}} = H,
         FakeH = {qlc_handle, {qlc_lc, Fun, {new_qlc_opt, a,b,c}}},
         {'EXIT', {{unsupported_qlc_handle,_},_}} = (catch qlc:e(FakeH))">>

     ],
    run(Config, Ts),
    ok.

eep37(Config) when is_list(Config) ->
    Ts = [
        <<"H = (fun _Handle() -> qlc:q([X || X <- []]) end)(),
           [] = qlc:eval(H)">>
    ],
    run(Config, Ts),
    ok.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bad_table_throw(Tab) ->
    Limit = 1,
    Select = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    PreFun = fun(_) -> throw({throw,bad_pre_fun}) end,
    PostFun = fun() -> throw({throw,bad_post_fun}) end,
    InfoFun = fun(Tag) -> info(Tab, Tag) end,
    qlc:table(Select, [{pre_fun,PreFun}, {post_fun, PostFun}, 
                       {info_fun, InfoFun}]).
    
bad_table_exit(Tab) ->
    Limit = 1,
    Select = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    PreFun = fun(_) -> erlang:error(bad_pre_fun) end,
    PostFun = fun() -> erlang:error(bad_post_fun) end,
    InfoFun = fun(Tag) -> info(Tab, Tag) end,
    qlc:table(Select, [{pre_fun,PreFun}, {post_fun, PostFun}, 
                       {info_fun, InfoFun}]).
    
info(_Tab, is_unique_objects) -> 
    false;
info(Tab, Tag) -> 
    try ets:info(Tab, Tag) catch _:_ -> undefined end.

create_ets(S, E) ->
    create_ets(lists:seq(S, E)).

create_ets(L) ->
    E1 = ets:new(e, []),
    true = ets:insert(E1, [{X,X} || X <- L]),
    E1.

etsc(F, Objs) ->
    etsc(F, [{keypos,1}], Objs).

etsc(F, Opts, Objs) ->
    E = ets:new(test, Opts),
    true = ets:insert(E, Objs),
    V = F(E),
    ets:delete(E),
    V.

join_info(H) ->
    {{qlc, S, Options}, Bs} = strip_qlc_call2(H),
    %% "Hide" the call to qlc_pt from the test in run_test().
    LoadedPT = code:is_loaded(qlc_pt),
    QH = qlc:string_to_handle(S, Options, Bs),
    _ = [unload_pt() || false <- [LoadedPT]], % doesn't take long...
    case {join_info_count(H), join_info_count(QH)} of
        {N, N} -> 
            N;
        Ns -> 
            Ns
    end.

strip_qlc_call(H) ->
    {Expr, _Bs} = strip_qlc_call2(H),
    Expr.

strip_qlc_call2(H) ->
    S = qlc:info(H, {flat, false}),
    {ok, Tokens, _EndLine} = erl_scan:string(S++".", 1, [text]),
    {ok, [Expr], Bs} = erl_eval:extended_parse_exprs(Tokens),
    {case Expr of
         {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC]} ->
             {qlc, lists:flatten([erl_pp:expr(LC), "."]), []};
         {call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC, Opts]} ->
             {qlc, lists:flatten([erl_pp:expr(LC), "."]),
              erl_parse:normalise(Opts)};
         {call,_,{remote,_,{atom,_,ets},{atom,_,match_spec_run}},_} ->
             {match_spec, Expr};
         {call,_,{remote,_,{atom,_,M},{atom,_,table}},_} ->
             {table, M, Expr};
         _ ->
             []
     end, Bs}.

-record(ji, {nmerge = 0, nlookup = 0, nnested_loop = 0, nkeysort = 0}).

%% Counts join options and (all) calls to qlc:keysort().
join_info_count(H) ->
    S = qlc:info(H, {flat, false}),    
    {ok, Tokens, _EndLine} = erl_scan:string(S++".", 1, [text]),
    {ok, [Expr], _Bs} = erl_eval:extended_parse_exprs(Tokens),
    #ji{nmerge = Nmerge, nlookup = Nlookup, 
        nkeysort = NKeysort, nnested_loop = Nnested_loop} = 
        ji(Expr, #ji{}),
    {Nmerge, Nlookup, Nnested_loop, NKeysort}.

ji({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[LC,Options]}, JI) ->
    NJI = case lists:keysearch(join, 1, erl_parse:normalise(Options)) of
              {value, {join, merge}} ->
                  JI#ji{nmerge = JI#ji.nmerge + 1};
              {value, {join, lookup}} ->
                  JI#ji{nlookup = JI#ji.nlookup + 1};
              {value, {join, nested_loop}} ->
                  JI#ji{nnested_loop = JI#ji.nnested_loop + 1};
              _  ->
                  JI
          end,
    ji(LC, NJI);
ji({call,_,{remote,_,{atom,_,qlc},{atom,_,keysort}},[_KP,H,_Options]}, JI) ->
    ji(H, JI#ji{nkeysort = JI#ji.nkeysort + 1});
ji(T, JI) when is_tuple(T) ->
    ji(tuple_to_list(T), JI);
ji([E | Es], JI) ->
    ji(Es, ji(E, JI));
ji(_, JI) ->
    JI.

%% Designed for ETS' and gb_table's format funs.
lookup_keys(Q) ->
    case lists:flatten(lookup_keys(i(Q), [])) of
        [] -> false;
        L -> lists:usort(L)
    end.

lookup_keys([Q | Qs], L) ->
    lookup_keys(Qs, lookup_keys(Q, L));
lookup_keys({qlc,_,Quals,_}, L) ->
    lookup_keys(Quals, L);
lookup_keys({list,Q,_}, L) ->
    lookup_keys(Q, L);
lookup_keys({generate,_,Q}, L) ->
    lookup_keys(Q, L);
lookup_keys({table,Chars}, L) when is_list(Chars) ->
    {ok, Tokens, _} = erl_scan:string(lists:flatten(Chars++"."), 1, [text]),
    {ok, [Expr], _Bs} = erl_eval:extended_parse_exprs(Tokens),
    case Expr of
        {call,_,_,[_fun,AKs]} ->
            case erl_parse:normalise(AKs) of
                Ks when is_list(Ks) ->
                    [lists:sort(Ks) | L];
                K -> % assume keys are never lists (ets only)
                    [K | L]
            end;
        _ -> % gb_table
            L
    end;
lookup_keys(_Q, L) ->
    L.

bad_table_format(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    FormatFun = {is, no, good},
    qlc:table(SelectFun, [{format_fun, FormatFun}]).

bad_table_format_arity(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    FormatFun = fun() -> {?MODULE, bad_table_format_arity, [Tab]} end,
    qlc:table(SelectFun, [{format_fun, FormatFun}]).

bad_table_traverse(Tab) ->
    Limit = 1,
    Select = fun(MS, _) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(Select, []).
    
bad_table_post(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(SelectFun, [{pre_fun,undefined}, 
                          {post_fun, fun(X) -> X end}, 
                          {info_fun, undefined}]).
    
bad_table_lookup(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(SelectFun, {lookup_fun, fun(X) -> X end}).

bad_table_max_lookup(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(SelectFun, {max_lookup, -2}).

bad_table_info_arity(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    InfoFun = fun() -> {?MODULE, bad_table_info_arity, [Tab]} end,
    qlc:table(SelectFun, [{info_fun, InfoFun}]).

default_table(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(SelectFun, [{format_fun, undefined},
                          {info_fun, undefined},
                          {lookup_fun, undefined},
                          {parent_fun, undefined},
                          {pre_fun,undefined}, 
                          {post_fun, undefined}]).
    
bad_table(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    qlc:table(SelectFun, [{info, fun() -> ok end}]).

bad_table_info_fun_n_objects(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    LookupFun = fun(_Pos, Ks) -> 
                        lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 
                end,
    InfoFun = fun(num_of_objects) -> exit(finito);
                 (_) -> undefined
              end,
    qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]).

bad_table_info_fun_indices(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    LookupFun = fun(_Pos, Ks) -> 
                        lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 
                end,
    InfoFun = fun(indices) -> throw({throw,apa});
                 (_) -> undefined
              end,
    qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]).

bad_table_info_fun_keypos(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    LookupFun = fun(_Pos, Ks) -> 
                        lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 
                end,
    InfoFun = fun(indices) -> erlang:error(keypos);
                  (_) -> undefined
              end,
    qlc:table(SelectFun, [{info_fun, InfoFun}, {lookup_fun, LookupFun}]).

bad_table_key_equality(Tab) ->
    Limit = 1,
    SelectFun = fun(MS) -> cb(ets:select(Tab, MS, Limit)) end,
    LookupFun = fun(_Pos, Ks) -> 
                        lists:flatmap(fun(K) -> ets:lookup(Tab, K) end, Ks) 
                end,
    qlc:table(SelectFun, [{lookup_fun, LookupFun},{key_equality,'=/='}]).

cb('$end_of_table') -> 
    [];
cb({Objects,Cont}) -> 
    Objects ++ fun() -> cb(ets:select(Cont)) end.

i(H) ->
    i(H, []).

i(H, Options) when is_list(Options) ->
    case has_format(Options) of
        true -> qlc:info(H, Options);
        false -> qlc:info(H, [{format, debug} | Options])
    end;
i(H, Option) ->
    i(H, [Option]).

has_format({format,_}) ->
    true;
has_format([E | Es]) ->
    has_format(E) or has_format(Es);
has_format(_) ->
    false.

format_info(H, Flat) ->
    L = qlc:info(H, [{flat, Flat}, {format,string}]),
    re:replace(L, "\s|\n|\t|\f|\r|\v", "", [{return,list},global]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% A list turned into a table...

table_kill_parent(List, Indices) ->
    ParentFun = fun() -> exit(self(), kill) end,
    table_i(List, Indices, ParentFun).

table_parent_throws(List, Indices) ->
    ParentFun = fun() -> throw({throw,thrown}) end,
    table_i(List, Indices, ParentFun).

table_parent_exits(List, Indices) ->
    ParentFun = fun() -> 1 + Indices end,
    table_i(List, Indices, ParentFun).

table_bad_parent_fun(List, Indices) ->
    ParentFun = fun(X) -> X end, % parent_fun should be nullary
    table_i(List, Indices, ParentFun).

table(List, Indices) ->
    ParentFun = fun() -> self() end,
    table_i(List, Indices, ParentFun).    

table(List, KeyPos, Indices) ->
    ParentFun = fun() -> self() end,
    table(List, Indices, KeyPos, ParentFun).    

table_i(List, Indices, ParentFun) ->
    table(List, Indices, undefined, ParentFun).

table(List, Indices, KeyPos, ParentFun) ->
    TraverseFun = fun() -> list_traverse(List) end,
    PreFun = fun(PreArgs) ->
                     {value, {parent_value, Pid}} = 
                         lists:keysearch(parent_value, 1, PreArgs),
                     true = is_pid(Pid)
             end,
    PostFun = fun() -> ok end,
    InfoFun = fun(indices) ->
                      Indices;
                 (is_unique_objects) ->
                      undefined;
                 (keypos) ->
                      KeyPos;
                 (num_of_objects) ->
                      undefined;
                 (_) ->
                      undefined
              end,
    LookupFun =
        fun(Column, Values) ->
                lists:flatmap(fun(V) ->
                                      case lists:keysearch(V, Column, List) of
                                          false -> [];
                                          {value,Val} -> [Val]
                                      end
                              end, Values)

                end,
    FormatFun = fun(all) ->
                        L = erl_anno:new(17),
                        {call,L,{remote,L,{atom,L,?MODULE},{atom,L,the_list}},
                                 [erl_parse:abstract(List, 17)]};
                   ({lookup, Column, Values}) ->
                        {?MODULE, list_keys, [Values, Column, List]}
                end,
    qlc:table(TraverseFun, [{info_fun,InfoFun}, {pre_fun, PreFun}, 
                            {post_fun, PostFun}, {lookup_fun, LookupFun}, 
                            {format_fun, FormatFun}, 
                            {parent_fun, ParentFun}]).

stop_list(List, Ets) ->
    Traverse = fun() -> list_traverse(List) end,
    PV = a_sample_parent_value,
    ParentFun = fun() -> PV end,
    Pre = fun(PreArgs) ->
                  {value, {parent_value, PV}} = 
                      lists:keysearch(parent_value, 1, PreArgs),
                  {value, {stop_fun, Fun}} = 
                      lists:keysearch(stop_fun, 1, PreArgs),
                  true = ets:insert(Ets, {stop_fun, Fun})
          end,
    qlc:table(Traverse, [{pre_fun, Pre}, {parent_fun, ParentFun}]).

list_traverse([]) ->
    [];
list_traverse([E | Es]) ->
    [E | fun() -> list_traverse(Es) end].

table_error(List, Error) ->
    table_error(List, undefined, Error).

table_error(List, KeyPos, Error) ->
    TraverseFun = fun() -> list_traverse2(lists:sort(List), Error) end,
    InfoFun = fun(is_sorted_key) -> true;
                 (keypos) -> KeyPos;
                 (_) -> undefined
              end,
    qlc:table(TraverseFun, [{info_fun,InfoFun}]).

list_traverse2([], Err) ->
    Err;
list_traverse2([E | Es], Err) ->
    [E | fun() -> list_traverse2(Es, Err) end].

table_lookup_error(List) ->
    TraverseFun = fun() -> list_traverse(List) end,
    LookupFun = fun(_Column, _Values) -> {error,lookup,failed} end,
    InfoFun = fun(keypos) -> 1;
                 (_) -> undefined
              end,
    qlc:table(TraverseFun, [{lookup_fun,LookupFun},{info_fun,InfoFun}]).

prep_scratchdir(Dir) ->
    put('$qlc_tmpdir', true),
    _ = filelib:ensure_dir(Dir),
    lists:foreach(fun(F) -> file:delete(F)
                  end, filelib:wildcard(filename:join(Dir, "*"))),
    true.

%% Truncate just once.
truncate_tmpfile(Dir, Where) ->
    case get('$qlc_tmpdir') of
        true -> 
            {ok, [TmpFile0 | _]} = file:list_dir(Dir),
            TmpFile = filename:join(Dir, TmpFile0),
            truncate(TmpFile, Where),
            erase('$qlc_tmpdir');
        _ ->
            true
    end.

truncate(File, Where) ->
    {ok, Fd} = file:open(File, [read, write]),
    {ok, _} = file:position(Fd, Where),
    ok = file:truncate(Fd),
    ok = file:close(Fd).

%% Crash just once.
crash_tmpfile(Dir, Where) ->
    case get('$qlc_tmpdir') of
        true -> 
            {ok, [TmpFile0 | _]} = file:list_dir(Dir),
            TmpFile = filename:join(Dir, TmpFile0),
            crash(TmpFile, Where),
            erase('$qlc_tmpdir');
        _ ->
            true
    end.

crash(File, Where) ->
    {ok, Fd} = file:open(File, [read, write]),
    {ok, _} = file:position(Fd, Where),
    ok = file:write(Fd, [10]),
    ok = file:close(Fd).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

run(Config, Tests) ->
    run(Config, [], Tests).

run(Config, Extra, Tests) ->
    lists:foreach(fun(Body) -> run_test(Config, Extra, Body) end, Tests).

run_test(Config, Extra, {cres, Body, ExpectedCompileReturn}) ->
    run_test(Config, Extra, {cres, Body, _Opts = [], ExpectedCompileReturn});
run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) ->
    {SourceFile, Mod} = compile_file_mod(Config),
    P = [Extra,<<"function() -> ">>, Body, <<", ok. ">>],
    CompileReturn = compile_file(Config, P, Opts),
    case comp_compare(ExpectedCompileReturn, CompileReturn) of
        true -> ok;
        false -> expected(ExpectedCompileReturn, CompileReturn, SourceFile)
    end,
    AbsFile = filename:rootname(SourceFile, ".erl"),
    _ = code:purge(Mod),
    {module, _} = code:load_abs(AbsFile, Mod),

    Ms0 = erlang:process_info(self(),messages),
    Before = {{lget(), lists:sort(ets:all()), Ms0}, pps()},

    %% Prepare the check that the qlc module does not call qlc_pt.
    _ = [unload_pt() || {file, Name} <- [code:is_loaded(qlc_pt)], 
                        Name =/= cover_compiled],

    R = case catch Mod:function() of
            {'EXIT', _Reason} = Error ->
                io:format("failed, got ~p~n", [Error]),
                fail(SourceFile);
            Reply ->
                Reply
        end,

    %% Check that the qlc module does not call qlc_pt:
    case code:is_loaded(qlc_pt) of
        {file, cover_compiled} ->
            ok;
        {file, _} ->
            io:format("qlc_pt was loaded in runtime~n", []),
            fail(SourceFile);
        false ->
            ok
    end,

    wait_for_expected(R, Before, SourceFile, true),
    code:purge(Mod);
run_test(Config, Extra, Body) ->
    run_test(Config, Extra, {cres,Body,[]}).

wait_for_expected(R, {Strict0,PPS0}=Before, SourceFile, Wait) ->
    Ms = erlang:process_info(self(),messages),
    After = {_,PPS1} = {{lget(), lists:sort(ets:all()), Ms}, pps()},
    case {R, After} of
        {ok, Before} ->
            ok;
        {ok, {Strict0,_}} ->
            {Ports0,Procs0} = PPS0,
            {Ports1,Procs1} = PPS1,
            case {Ports1 -- Ports0, Procs1 -- Procs0} of
                {[], []} -> ok;
                _ when Wait ->
                    timer:sleep(1000),
                    wait_for_expected(R, Before, SourceFile, false);
                {PortsDiff,ProcsDiff} ->
                    io:format("failure, got ~p~n, expected ~p\n",
                              [PPS1, PPS0]),
                    show("Old port", Ports0 -- Ports1),
                    show("New port", PortsDiff),
                    show("Old proc", Procs0 -- Procs1),
                    show("New proc", ProcsDiff),
                    fail(SourceFile)
            end;
        _ when Wait ->
            timer:sleep(1000),
            wait_for_expected(R, Before, SourceFile, false);
        _ ->
            expected({ok,Before}, {R,After}, SourceFile)
    end.

%% The qlc modules uses the process dictionary for storing names of files.
lget() ->
    lists:sort([T || {K, _} = T <- get(), is_qlc_key(K)]).

%% Copied from the qlc module.
-define(LCACHE_FILE(Ref), {Ref, '$_qlc_cache_tmpfiles_'}).
-define(MERGE_JOIN_FILE, '$_qlc_merge_join_tmpfiles_').

is_qlc_key(?LCACHE_FILE(_)) -> true;
is_qlc_key(?MERGE_JOIN_FILE) -> true;
is_qlc_key(_) -> false.

unload_pt() ->
    erlang:garbage_collect(), % get rid of references to qlc_pt...
    _ = code:purge(qlc_pt),
    _ = code:delete(qlc_pt).

compile_format(Config, Tests) ->
    Fun = fun(Test, Opts) ->
                  Return = compile_file(Config, Test, Opts),
                  format_messages(Return)
          end, 
    compile(Config, Tests, Fun).    

format_messages({warnings,Ws}) ->
    format_messages({errors,[],Ws});
format_messages({errors,Es,Ws}) ->
    {[format_msg(E, Mod) || {_Line,Mod,E} <- Es],
     [format_msg(W, Mod) || {_Line,Mod,W} <- Ws]}.

format_msg(Msg, Mod) ->
    IOlist = Mod:format_error(Msg),
    binary_to_list(iolist_to_binary(IOlist)).

compile(Config, Tests) ->
    Fun = fun(Test, Opts) -> catch compile_file(Config, Test, Opts) end,
    compile(Config, Tests, Fun).

compile(Config, Tests, Fun) ->
    F = fun({TestName,Test,Opts,Expected}, BadL) ->
                Return = Fun(Test, Opts),
                case comp_compare(Expected, Return) of
                    true ->
                        BadL;
                    false -> 
                        {File, _Mod} = compile_file_mod(Config),
                        expected(TestName, Expected, Return, File)
                end
        end,
    lists:foldl(F, [], Tests).

%% Compiles a test module and returns the list of errors and warnings.

compile_file(Config, Test0, Opts0) ->
    {File, Mod} = compile_file_mod(Config),
    Test = list_to_binary(["-module(", atom_to_list(Mod), "). "
                           "-import(qlc_SUITE, [i/1,i/2,format_info/2]). "
                           "-import(qlc_SUITE, [etsc/2, etsc/3]). "
                           "-import(qlc_SUITE, [create_ets/2]). "
                           "-import(qlc_SUITE, [strip_qlc_call/1]). "
                           "-import(qlc_SUITE, [join_info/1]). "
                           "-import(qlc_SUITE, [join_info_count/1]). "
                           "-import(qlc_SUITE, [lookup_keys/1]). "
                           "-include_lib(\"stdlib/include/qlc.hrl\"). ",
                           Test0]),
    Opts = [export_all,nowarn_export_all,return,nowarn_unused_record,{outdir,?privdir}|Opts0],
    ok = file:write_file(File, Test),
    case compile:file(File, Opts) of
        {ok, _M, Ws} -> warnings(File, Ws);
        {error, [{File,Es}], []} -> {errors, Es, []};
        {error, [{File,Es}], [{File,Ws}]} -> {errors, Es, Ws}
    end.

comp_compare(T, T) ->
    true;
comp_compare(T1, T2_0) ->
    T2 = wskip(T2_0),
    T1 =:= T2
       %% This clause should eventually be removed. 
       orelse ln(T1) =:= T2 orelse T1 =:= ln(T2).

wskip([]) ->
    [];
wskip([{_,sys_core_fold,{eval_failure,badarg}}|L]) ->
    wskip(L);
wskip([{{L,_C},sys_core_fold,M}|L]) ->
    [{L,sys_core_fold,M}|wskip(L)];
wskip({T,L}) ->
    {T,wskip(L)};
wskip([M|L]) ->
    [M|wskip(L)];
wskip(T) ->
    T.

%% Replaces locations like {Line,Column} with Line. 
ln({warnings,L}) ->
    {warnings,ln0(L)};
ln({errors,EL,WL}) ->
    {errors,ln0(EL),ln0(WL)};
ln(L) ->
    ln0(L).

ln0(L) ->
    lists:sort(ln1(L)).

ln1([]) ->
    [];
ln1([{File,Ms}|MsL]) when is_list(File) ->
    [{File,ln0(Ms)}|ln1(MsL)];
ln1([{{L,_C},Mod,Mess0}|Ms]) ->
    Mess = case Mess0 of
               {exported_var,V,{Where,{L1,_C1}}} ->
                   {exported_var,V,{Where,L1}};
               {unsafe_var,V,{Where,{L1,_C1}}} ->
                   {unsafe_var,V,{Where,L1}};
               %% There are more...
               M ->
                   M
           end,
    [{L,Mod,Mess}|ln1(Ms)];
ln1([M|Ms]) ->
    [M|ln1(Ms)].

%% -> {FileName, Module}; {string(), atom()}
compile_file_mod(Config) ->
    NameL = lists:concat([?TESTMODULE, "_", ?testcase]),
    Name = list_to_atom(NameL),
    File = filename(NameL ++ ".erl", Config),
    {File, Name}.

filename(Name, Config) when is_atom(Name) ->
    filename(atom_to_list(Name), Config);
filename(Name, Config) ->
    filename:join(?privdir, Name).

show(_S, []) ->
    ok;
show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) ->
    io:format("~s: ~w (~w), ~w: ~p~n",
              [S, Pid, proc_reg_name(Name), InitCall,
               erlang:process_info(Pid)]),
    show(S, Pids);
show(S, [{Port, _}|Ports]) when is_port(Port)->
    io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]),
    show(S, Ports).

pps() ->
    {port_list(), process_list()}.

port_list() ->
    [{P,safe_second_element(erlang:port_info(P, name))} || 
        P <- erlang:ports()].

process_list() ->
    [{P,process_info(P, registered_name),
      safe_second_element(process_info(P, initial_call))} || 
        P <- processes(), is_process_alive(P)].

proc_reg_name({registered_name, Name}) -> Name;
proc_reg_name([]) -> no_reg_name.

safe_second_element({_,Info}) -> Info;
safe_second_element(Other) -> Other.

warnings(File, Ws) ->
    case lists:append([W || {F, W} <- Ws, F =:= File]) of
        [] -> [];
        L -> {warnings, L}
    end.

expected(Test, Expected, Got, File) ->
    io:format("~nTest ~p failed. ", [Test]),
    expected(Expected, Got, File).

expected(Expected, Got, File) ->
    io:format("Expected~n  ~p~n, but got~n  ~p~n", [Expected, Got]),
    fail(File).

fail(Source) ->
    ct:fail({failed,testcase,on,Source}).

%% Copied from global_SUITE.erl.

install_error_logger() ->
    error_logger:add_report_handler(?MODULE, self()).

uninstall_error_logger() ->
    error_logger:delete_report_handler(?MODULE).

read_error_logger() ->
    receive
	{error, Why} ->
            {error, Why};
        {info, Why} ->
            {info, Why};
        {warning, Why} ->
            {warning, Why};
        {error, Pid, Tuple} ->
            {error, Pid, Tuple}
    after 1000 ->
	    io:format("No reply after 1 s\n", []),
	    ct:fail(failed)
    end.

%%-----------------------------------------------------------------
%% The error_logger handler used.
%% (Copied from stdlib/test/proc_lib_SUITE.erl.)
%%-----------------------------------------------------------------
init(Tester) ->
    {ok, Tester}.
    
handle_event({error, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
    Tester ! {error, Why},
    {ok, Tester};
handle_event({error, _GL, {_Pid, _Msg, [P, T]}}, Tester) when is_pid(P) ->
    Tester ! {error, P, T},
    {ok, Tester};
handle_event({info_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) ->
    Tester ! {info, Why},
    {ok, Tester};
handle_event({warning_msg, _GL, {_Pid, _Msg, [Why, _]}}, Tester) when is_atom(Why) ->
    Tester ! {warning, Why},
    {ok, Tester};
handle_event(_Event, State) ->
    {ok, State}.

handle_info(_, State) ->
    {ok, State}.

handle_call(_Query, State) -> {ok, {error, bad_query}, State}.

terminate(_Reason, State) ->
    State.