1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 1996-2013. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: -module(dets_SUITE).
   20: 
   21: %-define(debug, true).
   22: 
   23: -ifdef(debug).
   24: -define(format(S, A), io:format(S, A)).
   25: -define(config(X,Y), foo).
   26: -define(t, test_server).
   27: -define(privdir(_), "./dets_SUITE_priv").
   28: -define(datadir(_), "./dets_SUITE_data").
   29: -else.
   30: -include_lib("test_server/include/test_server.hrl").
   31: -define(format(S, A), ok).
   32: -define(privdir(Conf), ?config(priv_dir, Conf)).
   33: -define(datadir(Conf), ?config(data_dir, Conf)).
   34: -endif.
   35: 
   36: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   37: 	 init_per_group/2,end_per_group/2, 
   38: 	 newly_started/1, basic_v8/1, basic_v9/1,
   39: 	 open_v8/1, open_v9/1, sets_v8/1, sets_v9/1, bags_v8/1,
   40: 	 bags_v9/1, duplicate_bags_v8/1, duplicate_bags_v9/1,
   41: 	 access_v8/1, access_v9/1, dirty_mark/1, dirty_mark2/1,
   42: 	 bag_next_v8/1, bag_next_v9/1, oldbugs_v8/1, oldbugs_v9/1,
   43: 	 unsafe_assumptions/1, truncated_segment_array_v8/1,
   44: 	 truncated_segment_array_v9/1, open_file_v8/1, open_file_v9/1,
   45: 	 init_table_v8/1, init_table_v9/1, repair_v8/1, repair_v9/1,
   46: 	 hash_v8b_v8c/1, phash/1, fold_v8/1, fold_v9/1, fixtable_v8/1,
   47: 	 fixtable_v9/1, match_v8/1, match_v9/1, select_v8/1,
   48: 	 select_v9/1, update_counter/1, badarg/1, cache_sets_v8/1,
   49: 	 cache_sets_v9/1, cache_bags_v8/1, cache_bags_v9/1,
   50: 	 cache_duplicate_bags_v8/1, cache_duplicate_bags_v9/1,
   51: 	 otp_4208/1, otp_4989/1, many_clients/1, otp_4906/1, otp_5402/1,
   52:          simultaneous_open/1, insert_new/1, repair_continuation/1,
   53:          otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
   54:          otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
   55:          otp_8923/1, otp_9282/1, otp_11245/1]).
   56: 
   57: -export([dets_dirty_loop/0]).
   58: 
   59: -export([histogram/1, sum_histogram/1, ave_histogram/1]).
   60: 
   61: -export([init_per_testcase/2, end_per_testcase/2]).
   62: 
   63: %% Internal export.
   64: -export([client/2]).
   65: 
   66: -import(lists, 
   67: 	[append/1, delete/2, duplicate/2, filter/2, foreach/2, keysearch/3, 
   68: 	 last/1, map/2, member/2, reverse/1, seq/2, sort/1, usort/1]).
   69: 
   70: -include_lib("kernel/include/file.hrl").
   71: 
   72: -define(DETS_SERVER, dets).
   73: 
   74: %% HEADSZ taken from dets_v8.erl and dets_v9.erl.
   75: -define(HEADSZ_v8, 40).
   76: -define(HEADSZ_v9, (56+28*4+16)).
   77: -define(NO_KEYS_POS_v9, 36).
   78: -define(CLOSED_PROPERLY_POS, 8).
   79: 
   80: -define(NOT_PROPERLY_CLOSED,0).
   81: -define(CLOSED_PROPERLY,1).
   82: 
   83: init_per_testcase(_Case, Config) ->
   84:     Dog=?t:timetrap(?t:minutes(15)),
   85:     [{watchdog, Dog}|Config].
   86: 
   87: end_per_testcase(_Case, _Config) ->
   88:     Dog=?config(watchdog, _Config),
   89:     test_server:timetrap_cancel(Dog),
   90:     ok.
   91: 
   92: suite() -> [{ct_hooks,[ts_install_cth]}].
   93: 
   94: all() -> 
   95:     [
   96: 	basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
   97: 	bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
   98: 	newly_started, open_file_v8, open_file_v9,
   99: 	init_table_v8, init_table_v9, repair_v8, repair_v9,
  100: 	access_v8, access_v9, oldbugs_v8, oldbugs_v9,
  101: 	unsafe_assumptions, truncated_segment_array_v8,
  102: 	truncated_segment_array_v9, dirty_mark, dirty_mark2,
  103: 	bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
  104: 	fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
  105: 	select_v8, select_v9, update_counter, badarg,
  106: 	cache_sets_v8, cache_sets_v9, cache_bags_v8,
  107: 	cache_bags_v9, cache_duplicate_bags_v8,
  108: 	cache_duplicate_bags_v9, otp_4208, otp_4989,
  109: 	many_clients, otp_4906, otp_5402, simultaneous_open,
  110: 	insert_new, repair_continuation, otp_5487, otp_6206,
  111: 	otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
  112: 	otp_8899, otp_8903, otp_8923, otp_9282, otp_11245
  113:     ].
  114: 
  115: groups() -> 
  116:     [].
  117: 
  118: init_per_suite(Config) ->
  119:     Config.
  120: 
  121: end_per_suite(_Config) ->
  122:     ok.
  123: 
  124: init_per_group(_GroupName, Config) ->
  125:     Config.
  126: 
  127: end_per_group(_GroupName, Config) ->
  128:     Config.
  129: 
  130: newly_started(doc) ->
  131:     ["OTP-3621"];
  132: newly_started(suite) -> 
  133:     [];
  134: newly_started(Config) when is_list(Config) ->
  135:     true = is_alive(),
  136:     {ok, Node} = test_server:start_node(slave1, slave, []),
  137:     [] = rpc:call(Node, dets, all, []),
  138:     test_server:stop_node(Node),
  139:     ok.
  140: 
  141: basic_v8(doc) ->
  142:     ["Basic test case."];
  143: basic_v8(suite) -> 
  144:     [];
  145: basic_v8(Config) when is_list(Config) ->
  146:     basic(Config, 8).
  147: 
  148: basic_v9(doc) ->
  149:     ["Basic test case."];
  150: basic_v9(suite) -> 
  151:     [];
  152: basic_v9(Config) when is_list(Config) ->
  153:     basic(Config, 9).
  154: 
  155: basic(Config, Version) ->
  156:     Tab = dets_basic_test,
  157:     FName = filename(Tab, Config),
  158: 
  159:     P0 = pps(),
  160:     {ok, _} = dets:open_file(Tab,[{file, FName},{version,Version}]),
  161:     ok = dets:insert(Tab,{mazda,japan}),
  162:     ok = dets:insert(Tab,{toyota,japan}),
  163:     ok = dets:insert(Tab,{suzuki,japan}),
  164:     ok = dets:insert(Tab,{honda,japan}),
  165:     ok = dets:insert(Tab,{renault,france}),
  166:     ok = dets:insert(Tab,{citroen,france}),
  167:     ok = dets:insert(Tab,{opel,germany}),
  168:     ok = dets:insert(Tab,{saab,sweden}),
  169:     ok = dets:insert(Tab,{volvo,sweden}),
  170:     [{opel,germany}] = dets:lookup(Tab,opel),
  171:     Japs = dets:traverse(Tab, fun(Obj) ->
  172:                                       case Obj of
  173:                                           {_, japan} -> {continue, Obj};
  174:                                           _ -> continue
  175:                                       end
  176:                               end),
  177:     4  = length(Japs),
  178:     ok = dets:close(Tab),
  179:     file:delete(FName),
  180:     check_pps(P0),
  181:     ok.
  182:     
  183: 
  184: open_v8(doc) ->
  185:     [];
  186: open_v8(suite) -> 
  187:     [];
  188: open_v8(Config) when is_list(Config) ->
  189:     open(Config, 8).
  190: 
  191: open_v9(doc) ->
  192:     [];
  193: open_v9(suite) -> 
  194:     [];
  195: open_v9(Config) when is_list(Config) ->
  196:     open(Config, 9).
  197: 
  198: open(Config, Version) ->
  199:     %% Running this test twice means that the Dets server is restarted
  200:     %% twice. dets_sup specifies a maximum of 4 restarts in an hour.
  201:     %% If this becomes a problem, one should consider running this
  202:     %% test on a slave node.
  203: 
  204:     {Sets, Bags, Dups} = args(Config),
  205:     
  206:     All = Sets ++ Bags ++ Dups,
  207:     delete_files(All),
  208: 
  209:     Data = make_data(1),
  210: 
  211:     P0 = pps(),
  212:     Tabs = open_files(1, All, Version),
  213:     initialize(Tabs, Data),
  214:     check(Tabs, Data),
  215: 
  216:     foreach(fun(Tab) -> ok = dets:close(Tab) end, Tabs),
  217:     %% Now reopen the files
  218:     ?format("Reopening closed files \n", []),
  219:     Tabs = open_files(1, All, Version),
  220:     ?format("Checking contents of reopened files \n", []),
  221:     check(Tabs, Data),
  222:     %% crash the dets server
  223: 
  224:     ?format("Crashing dets server \n", []),
  225:     process_flag(trap_exit, true),
  226:     Procs = [whereis(?DETS_SERVER) | map(fun(Tab) -> dets:info(Tab, pid) end,
  227: 				 Tabs)],
  228:     foreach(fun(Pid) -> exit(Pid, kill) end, Procs),
  229:     timer:sleep(100),
  230:     c:flush(),  %% flush all the EXIT sigs
  231:     timer:sleep(200),
  232: 
  233:     %% Now reopen the files again
  234:     ?format("Reopening crashed files \n", []),
  235:     open_files(1, All, Version),
  236:     ?format("Checking contents of repaired files \n", []),
  237:     check(Tabs, Data),
  238:     
  239:     close_all(Tabs),
  240: 
  241:     delete_files(All),
  242:     P1 = pps(),
  243:     {Ports0, Procs0} = P0,
  244:     {Ports1, Procs1} = P1,
  245:     true = Ports1 =:= Ports0,
  246:     %% The dets_server process has been restarted:
  247:     [_] = Procs0 -- Procs1,
  248:     [_] = Procs1 -- Procs0,
  249:     ok.
  250:     
  251: check(Tabs, Data) ->
  252:     foreach(fun(Tab) ->
  253: 		    Kp = dets:info(Tab, keypos),
  254: 		    ?format("checking ~p~n", [Tab]),
  255: 		    foreach(fun(Item) ->
  256: 				    case dets:lookup(Tab, k(Kp,Item)) of
  257: 					[Item] -> ok;
  258: 					_Other -> bad(Tab,Item)
  259: 				    end
  260: 			    end, Data)
  261: 	    end, Tabs),
  262:     ok.
  263:     
  264: k(Kp, Obj) -> element(Kp, Obj).
  265: 
  266: bad(_Tab, _Item) ->
  267:     ?format("Can't find item ~p in ~p ~n", [_Item, _Tab]),
  268:     exit(badtab).
  269: 
  270: sets_v8(doc) ->
  271:     ["Performs traversal and match testing on set type dets tables."];
  272: sets_v8(suite) ->
  273:     [];
  274: sets_v8(Config) when is_list(Config) ->
  275:     sets(Config, 8).
  276: 
  277: sets_v9(doc) ->
  278:     ["Performs traversal and match testing on set type dets tables."];
  279: sets_v9(suite) ->
  280:     [];
  281: sets_v9(Config) when is_list(Config) ->
  282:     sets(Config, 9).
  283: 
  284: sets(Config, Version) ->
  285:     {Sets, _, _} = args(Config),
  286: 
  287:     Data = make_data(1),
  288:     delete_files(Sets),
  289:     P0 = pps(),
  290:     Tabs = open_files(1, Sets, Version),
  291:     Bigger = [{17,q,w,w}, {48,q,w,w,w,w,w,w}], % 48 requires a bigger buddy
  292:     initialize(Tabs, Data++Bigger++Data), % overwrite
  293:     Len = length(Data),
  294:     foreach(fun(Tab) -> trav_test(Data, Len, Tab) end, Tabs),
  295:     size_test(Len, Tabs),
  296:     no_keys_test(Tabs),
  297:     foreach(fun(Tab) -> del_test(Tab) end, Tabs),
  298:     initialize(Tabs, Data),
  299:     foreach(fun(Tab) -> del_obj_test(Tab) end, Tabs),
  300:     initialize(Tabs, Data),
  301:     foreach(fun(Tab) ->
  302:                     Len = dets:info(Tab, size) end,
  303:             Tabs),
  304:     foreach(fun(Tab) -> match_test(Data, Tab) end, Tabs),
  305:     foreach(fun(Tab) -> match_del_test(Tab) end, Tabs),
  306:     
  307:     close_all(Tabs),
  308:     delete_files(Sets),
  309:     check_pps(P0),
  310:     ok.
  311: 
  312: bags_v8(doc) ->
  313:     ["Performs traversal and match testing on bag type dets tables."];
  314: bags_v8(suite) ->
  315:     [];
  316: bags_v8(Config) when is_list(Config) ->
  317:     bags(Config, 8).
  318: 
  319: bags_v9(doc) ->
  320:     ["Performs traversal and match testing on bag type dets tables."];
  321: bags_v9(suite) ->
  322:     [];
  323: bags_v9(Config) when is_list(Config) ->
  324:     bags(Config, 9).
  325: 
  326: bags(Config, Version) ->
  327:     {_, Bags, _} = args(Config),
  328:     Data = make_data(1, bag),  %% gives twice as many objects
  329:     delete_files(Bags),
  330:     P0 = pps(),
  331:     Tabs = open_files(1, Bags, Version),
  332:     initialize(Tabs, Data++Data),
  333:     Len = length(Data),
  334:     foreach(fun(Tab) -> trav_test(Data, Len, Tab) end, Tabs),
  335:     size_test(Len, Tabs),
  336:     no_keys_test(Tabs),
  337:     foreach(fun(Tab) -> del_test(Tab) end, Tabs),
  338:     initialize(Tabs, Data),
  339:     foreach(fun(Tab) -> del_obj_test(Tab) end, Tabs),
  340:     initialize(Tabs, Data),
  341:     foreach(fun(Tab) ->
  342:                     Len = dets:info(Tab, size) end,
  343:             Tabs),
  344:     foreach(fun(Tab) -> match_test(Data, Tab) end, Tabs),
  345:     foreach(fun(Tab) -> match_del_test(Tab) end, Tabs),
  346:     close_all(Tabs),
  347:     delete_files(Bags),
  348:     check_pps(P0),
  349:     ok.
  350: 
  351: 
  352: duplicate_bags_v8(doc) ->
  353:    ["Performs traversal and match testing on duplicate_bag type dets tables."];
  354: duplicate_bags_v8(suite) ->
  355:     [];
  356: duplicate_bags_v8(Config) when is_list(Config) ->
  357:     duplicate_bags(Config, 8).
  358: 
  359: duplicate_bags_v9(doc) ->
  360:    ["Performs traversal and match testing on duplicate_bag type dets tables."];
  361: duplicate_bags_v9(suite) ->
  362:     [];
  363: duplicate_bags_v9(Config) when is_list(Config) ->
  364:     duplicate_bags(Config, 9).
  365: 
  366: duplicate_bags(Config, Version) when is_list(Config) ->
  367:     {_, _, Dups} = args(Config),
  368:     Data = make_data(1, duplicate_bag), %% gives twice as many objects
  369:     delete_files(Dups),
  370:     P0 = pps(),
  371:     Tabs = open_files(1, Dups, Version),
  372:     initialize(Tabs, Data),
  373:     Len = length(Data),
  374:     foreach(fun(Tab) -> trav_test(Data, Len, Tab) end, Tabs),
  375:     size_test(Len, Tabs),
  376:     no_keys_test(Tabs),
  377:     foreach(fun(Tab) -> del_test(Tab) end, Tabs),
  378:     initialize(Tabs, Data),
  379:     foreach(fun(Tab) -> del_obj_test(Tab) end, Tabs),
  380:     initialize(Tabs, Data),
  381:     foreach(fun(Tab) ->
  382:                     Len = dets:info(Tab, size) end,
  383:             Tabs),
  384:     foreach(fun(Tab) -> match_test(Data, Tab) end, Tabs),
  385:     foreach(fun(Tab) -> match_del_test(Tab) end, Tabs),
  386:     close_all(Tabs),
  387:     delete_files(Dups),
  388:     check_pps(P0),
  389:     ok.
  390: 
  391: 
  392: access_v8(doc) ->
  393:     [];
  394: access_v8(suite) ->
  395:     [];
  396: access_v8(Config) when is_list(Config) ->
  397:     access(Config, 8).
  398: 
  399: access_v9(doc) ->
  400:     [];
  401: access_v9(suite) ->
  402:     [];
  403: access_v9(Config) when is_list(Config) ->
  404:     access(Config, 9).
  405: 
  406: access(Config, Version) ->
  407:     Args_acc = [[{ram_file, true}, {access, read}],
  408: 		[{access, read}]],
  409:     Args = [[{ram_file, true}],
  410: 	    []],
  411:     
  412:     {Args_acc_1, _, _} = zip_filename(Args_acc, [], [], Config),
  413:     delete_files(Args_acc_1),
  414:     {Args_1, _, _} = zip_filename(Args, [], [], Config),
  415: 
  416:     P0 = pps(),
  417:     {error, {file_error,_,enoent}} = dets:open_file('1', hd(Args_acc_1)),
  418: 
  419:     Tabs = open_files(1, Args_1, Version),
  420:     close_all(Tabs),
  421:     Tabs = open_files(1, Args_acc_1, Version),
  422: 
  423:     foreach(fun(Tab) ->
  424:                     {error, {access_mode,_}} = dets:insert(Tab, {1,2}),
  425:                     [] = dets:lookup(Tab, 11),
  426:                     '$end_of_table' = dets:first(Tab),
  427:                     {error, {access_mode,_}} = dets:delete(Tab, 22)
  428:             end, Tabs),
  429:     close_all(Tabs),
  430:     delete_files(Args_acc_1),
  431:     check_pps(P0),
  432:     ok.
  433: 
  434: 
  435: dirty_mark(doc) ->
  436:     ["Test that the table is not marked dirty if not written"];
  437: dirty_mark(suite) ->
  438:     [];
  439: dirty_mark(Config) when is_list(Config) ->
  440:     true = is_alive(),
  441:     Tab = dets_dirty_mark_test,
  442:     FName = filename(Tab, Config),
  443:     P0 = pps(),
  444:     dets:open_file(Tab,[{file, FName}]),
  445:     dets:insert(Tab,{mazda,japan}),
  446:     dets:insert(Tab,{toyota,japan}),
  447:     dets:insert(Tab,{suzuki,japan}),
  448:     dets:insert(Tab,{honda,japan}),
  449:     dets:insert(Tab,{renault,france}),
  450:     dets:insert(Tab,{citroen,france}),
  451:     dets:insert(Tab,{opel,germany}),
  452:     dets:insert(Tab,{saab,sweden}),
  453:     dets:insert(Tab,{volvo,sweden}),
  454:     [{opel,germany}] = dets:lookup(Tab,opel),
  455:     ok = dets:close(Tab),
  456:     Call = fun(P,A) ->
  457: 		   P ! {self(), A},
  458: 		   receive
  459: 		       {P, Ans} ->
  460: 			   Ans
  461: 		   after 5000 ->
  462: 			   exit(other_process_dead)
  463: 		   end
  464: 	   end,
  465:     {ok, Node} = test_server:start_node(dets_dirty_mark,
  466:                                         slave,
  467:                                         [{linked, false},
  468:                                          {args, "-pa " ++
  469:                                               filename:dirname
  470: 						(code:which(?MODULE))}]),
  471:     ok = ensure_node(20, Node),
  472:     %% io:format("~p~n",[rpc:call(Node, code, get_path, [])]),
  473:     %% io:format("~p~n",[rpc:call(Node, file, get_cwd, [])]),
  474:     %% io:format("~p~n",[Config]),
  475:     Pid = rpc:call(Node,erlang, spawn,
  476: 			 [?MODULE, dets_dirty_loop, []]),
  477:     {ok, Tab} = Call(Pid, [open, Tab, [{file, FName}]]),
  478:     [{opel,germany}] = Call(Pid, [read,Tab,opel]),
  479:     test_server:stop_node(Node),
  480:     {ok, Tab} = dets:open_file(Tab,[{file, FName},
  481:                                     {repair,false}]),
  482:     ok = dets:close(Tab),
  483:     file:delete(FName),
  484:     check_pps(P0),
  485:     ok.
  486: 
  487: dirty_mark2(doc) ->
  488:     ["Test that the table is flushed when auto_save is in effect"];
  489: dirty_mark2(suite) ->
  490:     [];
  491: dirty_mark2(Config) when is_list(Config) ->
  492:     true = is_alive(),
  493:     Tab = dets_dirty_mark2_test,
  494:     FName = filename(Tab, Config),
  495:     P0 = pps(),
  496:     dets:open_file(Tab,[{file, FName}]),
  497:     dets:insert(Tab,{toyota,japan}),
  498:     dets:insert(Tab,{suzuki,japan}),
  499:     dets:insert(Tab,{honda,japan}),
  500:     dets:insert(Tab,{renault,france}),
  501:     dets:insert(Tab,{citroen,france}),
  502:     dets:insert(Tab,{opel,germany}),
  503:     dets:insert(Tab,{saab,sweden}),
  504:     dets:insert(Tab,{volvo,sweden}),
  505:     [{opel,germany}] = dets:lookup(Tab,opel),
  506:     ok = dets:close(Tab),
  507:     Call = fun(P,A) ->
  508: 		   P ! {self(), A},
  509: 		   receive
  510: 		       {P, Ans} ->
  511: 			   Ans
  512: 		   after 5000 ->
  513: 			   exit(other_process_dead)
  514: 		   end
  515: 	   end,
  516:     {ok, Node} = test_server:start_node(dets_dirty_mark2,
  517:                                         slave,
  518:                                         [{linked, false},
  519:                                          {args, "-pa " ++
  520:                                               filename:dirname
  521: 						(code:which(?MODULE))}]),
  522:     ok = ensure_node(20, Node),
  523:     Pid = rpc:call(Node,erlang, spawn,
  524:                    [?MODULE, dets_dirty_loop, []]),
  525:     {ok, Tab} = Call(Pid, [open, Tab, [{file, FName},{auto_save,1000}]]),
  526:     ok = Call(Pid, [write,Tab,{mazda,japan}]),
  527:     timer:sleep(2100),
  528:     %% Read something, just to give auto save time to finish.
  529:     [{opel,germany}] = Call(Pid, [read,Tab,opel]),
  530:     test_server:stop_node(Node),
  531:     {ok, Tab} = dets:open_file(Tab, [{file, FName}, {repair,false}]),
  532:     ok = dets:close(Tab),
  533:     file:delete(FName),
  534:     check_pps(P0),
  535:     ok.
  536: 
  537: dets_dirty_loop() ->
  538:     receive 
  539: 	{From, [open, Name, Args]} ->
  540: 	    Ret = dets:open_file(Name, Args),
  541: 	    From ! {self(), Ret},
  542: 	    dets_dirty_loop();
  543: 	{From, [read, Name, Key]} ->
  544: 	    Ret = dets:lookup(Name, Key),
  545: 	    From ! {self(), Ret},
  546: 	    dets_dirty_loop();
  547: 	{From, [write, Name, Value]} ->
  548: 	    Ret = dets:insert(Name, Value),
  549: 	    From ! {self(), Ret},
  550: 	    dets_dirty_loop();
  551:         {From, [close, Name]} ->
  552:             Ret = dets:close(Name),
  553:             From ! {self(), Ret},
  554:             dets_dirty_loop()
  555:     end.
  556: 
  557: 
  558: bag_next_v8(suite) ->
  559:     [];
  560: bag_next_v8(doc) ->
  561:     ["Check that bags and next work as expected."];
  562: bag_next_v8(Config) when is_list(Config) ->
  563:     bag_next(Config, 8).
  564: 
  565: bag_next_v9(suite) ->
  566:     [];
  567: bag_next_v9(doc) ->
  568:     ["Check that bags and next work as expected."];
  569: bag_next_v9(Config) when is_list(Config) ->
  570:     Tab = dets_bag_next_test,
  571:     FName = filename(Tab, Config),
  572: 
  573:     %% first and next crash upon error
  574:     dets:open_file(Tab,[{file, FName}, {type, bag},{version,9}]),
  575:     ok = dets:insert(Tab, [{1,1},{2,2},{3,3},{4,4}]),
  576:     FirstKey = dets:first(Tab),
  577:     NextKey = dets:next(Tab, FirstKey),
  578:     [FirstObj | _] = dets:lookup(Tab, FirstKey),
  579:     [NextObj | _] = dets:lookup(Tab, NextKey),
  580:     {ok, FirstPos} = dets:where(Tab, FirstObj),
  581:     {ok, NextPos} = dets:where(Tab, NextObj),
  582:     crash(FName, NextPos+12),
  583:     {'EXIT',BadObject1} = (catch dets:next(Tab, FirstKey)),
  584:     bad_object(BadObject1, FName),
  585:     crash(FName, FirstPos+12),
  586:     {'EXIT',BadObject2} = (catch dets:first(Tab)),
  587:     bad_object(BadObject2, FName),
  588:     dets:close(Tab),
  589:     file:delete(FName),
  590: 
  591:     bag_next(Config, 9).
  592: 
  593: bag_next(Config, Version) ->
  594:     Tab = dets_bag_next_test,
  595:     FName = filename(Tab, Config),
  596:     P0 = pps(),
  597:     dets:open_file(Tab,[{file, FName}, {type, bag},{version,Version}]),
  598:     dets:insert(Tab,{698,hopp}),
  599:     dets:insert(Tab,{186,hopp}),
  600:     dets:insert(Tab,{hej,hopp}),
  601:     dets:insert(Tab,{186,plopp}),
  602:     Loop = fun(N, Last, Self) ->
  603: 		   case N of
  604: 		       0 ->
  605: 			   exit({unterminated_first_next_sequence, N, Last});
  606: 		       _ ->
  607: 			   case Last of
  608: 			       '$end_of_table' ->
  609: 				   ok;
  610: 			       _ ->
  611: 				   Self(N-1, dets:next(Tab,Last), Self)
  612: 			   end
  613: 		   end
  614: 	   end,
  615:     ok = Loop(4,dets:first(Tab),Loop),
  616:     dets:close(Tab),
  617:     file:delete(FName),
  618:     check_pps(P0),
  619:     ok.
  620: 
  621: oldbugs_v8(doc) ->
  622:     [];
  623: oldbugs_v8(suite) ->
  624:     [];
  625: oldbugs_v8(Config) when is_list(Config) ->
  626:     oldbugs(Config, 8).
  627: 
  628: oldbugs_v9(doc) ->
  629:     [];
  630: oldbugs_v9(suite) ->
  631:     [];
  632: oldbugs_v9(Config) when is_list(Config) ->
  633:     oldbugs(Config, 9).
  634: 
  635: oldbugs(Config, Version) ->
  636:     FName = filename(dets_suite_oldbugs_test, Config),
  637:     P0 = pps(),
  638:     {ok, ob} = dets:open_file(ob, [{version, Version},
  639: 					 {type, bag}, {file, FName}]),
  640:     ok = dets:insert(ob, {1, 2}),
  641:     ok = dets:insert(ob, {1,3}),
  642:     ok = dets:insert(ob, {1, 2}),
  643:     2 = dets:info(ob, size),  %% assertion
  644:     ok = dets:close(ob),
  645:     file:delete(FName),
  646:     check_pps(P0),
  647:     ok.
  648: 
  649: unsafe_assumptions(suite) -> [];
  650: unsafe_assumptions(doc) ->
  651:     "Tests that shrinking an object and then expanding it works.";
  652: unsafe_assumptions(Config) when is_list(Config) ->
  653:     FName = filename(dets_suite_unsafe_assumptions_test, Config),
  654:     file:delete(FName),
  655:     P0 = pps(),
  656:     {ok, a} = dets:open_file(a, [{version,8},{file, FName}]),
  657:     O0 = {2,false},
  658:     O1 = {1, false},
  659:     O2 = {1, true},
  660:     O3 = {1, duplicate(20,false)},
  661:     O4 = {1, duplicate(25,false)}, % same 2-log as O3
  662:     ok = dets:insert(a, O1),
  663:     ok = dets:insert(a, O0),
  664:     true = [O1,O0] =:= sort(get_all_objects(a)),
  665:     true = [O1,O0] =:= sort(get_all_objects_fast(a)),
  666:     ok = dets:insert(a, O2),
  667:     true = [O2,O0] =:= sort(get_all_objects(a)),
  668:     true = [O2,O0] =:= sort(get_all_objects_fast(a)),
  669:     ok = dets:insert(a, O3),
  670:     true = [O3,O0] =:= sort(get_all_objects(a)),
  671:     true = [O3,O0] =:= sort(get_all_objects_fast(a)),
  672:     ok = dets:insert(a, O4),
  673:     true = [O4,O0] =:= sort(get_all_objects(a)),
  674:     true = [O4,O0] =:= sort(get_all_objects_fast(a)),
  675:     ok = dets:close(a),
  676:     file:delete(FName),
  677:     check_pps(P0),
  678:     ok.
  679: 
  680: truncated_segment_array_v8(suite) -> [];
  681: truncated_segment_array_v8(doc) ->
  682:     "Tests that a file where the segment array has been truncated "
  683:     "is possible to repair.";
  684: truncated_segment_array_v8(Config) when is_list(Config) ->
  685:     trunc_seg_array(Config, 8).
  686: 
  687: truncated_segment_array_v9(suite) -> [];
  688: truncated_segment_array_v9(doc) ->
  689:     "Tests that a file where the segment array has been truncated "
  690:     "is possible to repair.";
  691: truncated_segment_array_v9(Config) when is_list(Config) ->
  692:     trunc_seg_array(Config, 9).
  693: 
  694: trunc_seg_array(Config, V) ->
  695:     TabRef = dets_suite_truncated_segment_array_test,
  696:     Fname = filename(TabRef, Config),
  697:     %% Create file that needs to be repaired
  698:     file:delete(Fname),
  699:     P0 = pps(),
  700:     {ok, TabRef} = dets:open_file(TabRef, [{file, Fname},{version,V}]),
  701:     ok = dets:close(TabRef),
  702:     
  703:     %% Truncate the file
  704:     HeadSize = headsz(V),
  705:     truncate(Fname, HeadSize + 10),
  706:     
  707:     %% Open the truncated file
  708:     io:format("Expect repair:~n"),
  709:     {ok, TabRef} = dets:open_file(TabRef,
  710: 					[{file, Fname}, {repair, true}]),
  711:     ok = dets:close(TabRef),
  712:     file:delete(Fname),
  713:     check_pps(P0),
  714:     ok.
  715: 
  716: open_file_v8(doc) ->
  717:     ["open_file/1 test case."];
  718: open_file_v8(suite) -> 
  719:     [];
  720: open_file_v8(Config) when is_list(Config) ->
  721:     open_1(Config, 8).
  722: 
  723: open_file_v9(doc) ->
  724:     ["open_file/1 test case."];
  725: open_file_v9(suite) -> 
  726:     [];
  727: open_file_v9(Config) when is_list(Config) ->
  728:     T = open_v9,
  729:     Fname = filename(T, Config),
  730:     {ok, _} = dets:open_file(T, [{file,Fname},{version,9}]),
  731:     9 = dets:info(T, version),
  732:     true = [self()] =:= dets:info(T, users),
  733:     {ok, _} = dets:open_file(T, [{file,Fname},{version,9}]),
  734:     {error,incompatible_arguments} =
  735: 	dets:open_file(T, [{file,Fname},{version,8}]),
  736:     true = [self(),self()] =:= dets:info(T, users),
  737:     ok = dets:close(T),
  738:     true = [self()] =:= dets:info(T, users),
  739:     ok = dets:close(T),
  740:     undefined = ets:info(T, users),
  741:     file:delete(Fname),
  742: 
  743:     open_1(Config, 9).
  744: 
  745: open_1(Config, V) ->
  746:     TabRef = open_file_1_test,
  747:     Fname = filename(TabRef, Config),
  748:     file:delete(Fname),
  749: 
  750:     P0 = pps(),
  751:     {error,{file_error,Fname,enoent}} = dets:open_file(Fname),
  752:     
  753:     ok = file:write_file(Fname, duplicate(100,65)),
  754:     {error,{not_a_dets_file,Fname}} = dets:open_file(Fname),
  755:     file:delete(Fname),
  756: 
  757:     HeadSize = headsz(V),
  758:     {ok, TabRef} = dets:open_file(TabRef, [{file, Fname},{version,V}]),
  759:     ok = dets:close(TabRef),
  760:     truncate(Fname, HeadSize + 10),
  761:     true = dets:is_dets_file(Fname),
  762:     io:format("Expect repair:~n"),
  763:     {ok, Ref} = dets:open_file(Fname), % repairing
  764:     ok = dets:close(Ref),
  765:     file:delete(Fname),
  766: 
  767:     %% truncated file header, invalid type
  768:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
  769:     ok = ins(TabRef, 3000),
  770:     ok = dets:close(TabRef),
  771:     TypePos = 12,
  772:     crash(Fname, TypePos),
  773:     {error, {invalid_type_code,Fname}} = dets:open_file(Fname),
  774:     truncate(Fname, HeadSize - 10),
  775:     {error, {tooshort,Fname}} = dets:open_file(Fname),
  776:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
  777:     ok = dets:close(TabRef),
  778:     file:delete(Fname),
  779: 
  780:     {error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}),
  781:     check_pps(P0),
  782:     ok.
  783: 
  784: init_table_v8(doc) ->
  785:     ["initialize_table/2 and from_ets/2 test case."];
  786: init_table_v8(suite) -> 
  787:     [];
  788: init_table_v8(Config) when is_list(Config) ->
  789:     init_table(Config, 8).
  790: 
  791: init_table_v9(doc) ->
  792:     ["initialize_table/2 and from_ets/2 test case."];
  793: init_table_v9(suite) -> 
  794:     [];
  795: init_table_v9(Config) when is_list(Config) ->
  796:     %% Objects are returned in "time order".
  797:     T = init_table_v9,
  798:     Fname = filename(T, Config),
  799:     file:delete(Fname),
  800:     L = [{1,a},{2,b},{1,c},{2,c},{1,c},{2,a},{1,b}],
  801:     Input = init([L]),
  802:     {ok, _} = dets:open_file(T, [{file,Fname},{version,9},
  803: 				       {type,duplicate_bag}]),
  804:     ok = dets:init_table(T, Input),
  805:     [{1,a},{1,c},{1,c},{1,b}] = dets:lookup(T, 1),
  806:     [{2,b},{2,c},{2,a}] = dets:lookup(T, 2),
  807:     ok = dets:close(T),
  808:     file:delete(Fname),
  809: 
  810:     init_table(Config, 9),
  811:     fast_init_table(Config).
  812: 
  813: init_table(Config, V) ->
  814:     TabRef = init_table_test,
  815:     Fname = filename(TabRef, Config),
  816:     file:delete(Fname),
  817:     P0 = pps(),
  818: 
  819:     Args = [{file,Fname},{version,V},{auto_save,120000}],
  820:     {ok, _} = dets:open_file(TabRef, Args),
  821:     {'EXIT', _} =
  822: 	(catch dets:init_table(TabRef, fun(foo) -> bar end)),
  823:     dets:close(TabRef),
  824:     {ok, _} = dets:open_file(TabRef, Args),
  825:     {'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end)),
  826:     dets:close(TabRef),
  827:     {ok, _} = dets:open_file(TabRef, Args),
  828:     {'EXIT', {badarg, _}} = (catch dets:init_table(TabRef, nofun)),
  829:     {'EXIT', {badarg, _}} =
  830: 	(catch dets:init_table(TabRef, fun(_X) -> end_of_input end, 
  831: 			       [{foo,bar}])),
  832:     dets:close(TabRef),
  833:     {ok, _} = dets:open_file(TabRef, Args),
  834:     away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end)),
  835:     dets:close(TabRef),
  836:     {ok, _} = dets:open_file(TabRef, Args),
  837:     {error, {init_fun, fopp}} =
  838: 	dets:init_table(TabRef, fun(read) -> fopp end),
  839:     dets:close(TabRef),
  840: 
  841:     {ok, _} = dets:open_file(TabRef, Args),
  842:     dets:safe_fixtable(TabRef, true),
  843:     {error, {fixed_table, TabRef}} = dets:init_table(TabRef, init([])),
  844:     dets:safe_fixtable(TabRef, false),
  845:     ET = ets:new(foo,[]),
  846:     ok = dets:from_ets(TabRef, ET),
  847:     [] = get_all_objects(TabRef),
  848:     [] = get_all_objects_fast(TabRef),
  849:     true = ets:insert(ET, {1,a}),
  850:     true = ets:insert(ET, {2,b}),
  851:     ok = dets:from_ets(TabRef, ET),
  852:     [{1,a},{2,b}] = sort(get_all_objects(TabRef)),
  853:     [{1,a},{2,b}] = sort(get_all_objects_fast(TabRef)),
  854:     true = ets:delete(ET),
  855:     120000 = dets:info(TabRef, auto_save),
  856:     ok = dets:close(TabRef),
  857: 
  858:     {ok, _} = dets:open_file(TabRef, [{access,read} | Args]),
  859:     {error, {access_mode, Fname}} = dets:init_table(TabRef, init([])),
  860:     ok = dets:close(TabRef),
  861: 
  862:     {ok, _} = dets:open_file(TabRef, Args),
  863:     {error, invalid_objects_list} =
  864: 	(catch dets:init_table(TabRef, init([[{1,2},bad,{3,4}]]))),
  865:     _ = dets:close(TabRef),
  866:     file:delete(Fname),
  867: 
  868:     L1 = [[{1,a},{2,b}],[],[{3,c}],[{4,d}],[]],
  869:     bulk_init(L1, set, 4, Config, V),
  870:     L2 = [[{1,a},{2,b}],[],[{2,q},{3,c}],[{4,d}],[{4,e},{2,q}]],
  871:     bulk_init(L2, set, 4, Config, V),
  872:     bulk_init(L2, bag, 6, Config, V),
  873:     bulk_init(L2, duplicate_bag, 7, Config, V),
  874:     bulk_init(L1, set, 4, 512, Config, V),
  875:     bulk_init([], set, 0, 10000, Config, V),
  876:     file:delete(Fname),
  877: 
  878:     %% Initiate a file that contains a lot of objects.
  879:     {ok, _} = dets:open_file(TabRef, [{min_no_slots,10000} | Args]),
  880:     ok = ins(TabRef, 6000),
  881:     Fun = init_fun(0, 10000),
  882:     ok = dets:init_table(TabRef, Fun,{format,term}),
  883:     All = sort(get_all_objects(TabRef)),
  884:     FAll = get_all_objects_fast(TabRef),
  885:     true = All =:= sort(FAll),
  886:     true = length(All) =:= 10000,
  887:     ok = dets:close(TabRef),
  888:     file:delete(Fname),
  889: 
  890:     {ok, _} = dets:open_file(TabRef, [{min_no_slots,4000} | Args]),
  891:     ok = ins(TabRef, 6000),
  892:     FileSize1 = dets:info(TabRef, file_size),
  893:     Fun2 = init_fun(0, 4000),
  894:     ok = dets:init_table(TabRef, Fun2),
  895:     FileSize2 = dets:info(TabRef, file_size),
  896:     ok = dets:close(TabRef),
  897:     true = FileSize1 > FileSize2,
  898:     file:delete(Fname),
  899: 
  900:     check_pps(P0),
  901:     ok.
  902: 
  903: bulk_init(Ls, Type, N, Config, V) ->
  904:     bulk_init(Ls, Type, N, 256, Config, V).
  905: 
  906: bulk_init(Ls, Type, N, Est, Config, V) ->
  907:     T = init_table_test,
  908:     Fname = filename(T, Config),
  909:     file:delete(Fname),
  910:     Input = init(Ls),
  911:     Args = [{ram_file,false}, {type,Type},{keypos,1},{file,Fname},
  912: 	    {estimated_no_objects, Est},{version,V}],
  913:     {ok, T} = dets:open_file(T, Args),
  914:     ok = dets:init_table(T, Input),
  915:     All = sort(get_all_objects(T)),
  916:     FAll = get_all_objects_fast(T),
  917:     true = All =:= sort(FAll),
  918:     true = length(All) =:= N,
  919:     true = dets:info(T, size) =:= N,
  920:     ok = dets:close(T),
  921:     
  922:     {ok, T} = dets:open_file(T, Args),
  923:     All2 = sort(get_all_objects(T)),
  924:     FAll2 = get_all_objects_fast(T),
  925:     true = All =:= All2,
  926:     true = All =:= sort(FAll2),
  927:     ok = dets:close(T),
  928:     file:delete(Fname).
  929: 
  930: init(L) ->
  931:     fun(close) ->
  932: 	    ok;
  933:        (read) when [] =:= L ->
  934: 	    end_of_input;
  935:        (read) ->
  936: 	    [E | Es] = L,
  937: 	    {E, init(Es)}
  938:     end.
  939: 
  940: init_fun(I, N) ->
  941:     fun(read) when I =:= N ->
  942: 	    end_of_input;
  943:        (read) ->
  944: 	    {NewN, Items} = items(I, N, 1000, []),
  945: 	    {Items, init_fun(NewN, N)};
  946:        (close) ->
  947: 	    ignored
  948:     end.
  949: 
  950: fast_init_table(Config) ->
  951:     V = 9,
  952:     TabRef = init_table_test,
  953:     Fname = filename(TabRef, Config),
  954:     file:delete(Fname),
  955:     P0 = pps(),
  956: 
  957:     Args = [{file,Fname},{version,V},{auto_save,120000}],
  958: 
  959:     Source = init_table_test_source,
  960:     SourceFname = filename(Source, Config),
  961:     file:delete(SourceFname),
  962:     SourceArgs = [{file,SourceFname},{version,V},{auto_save,120000}],
  963: 
  964:     {ok, Source} = dets:open_file(Source, SourceArgs),
  965:     
  966:     {ok, _} = dets:open_file(TabRef, Args),
  967:     {'EXIT', _} =
  968: 	(catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})),
  969:     dets:close(TabRef),
  970:     {ok, _} = dets:open_file(TabRef, Args),
  971:     {'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end,
  972: 					       {format,bchunk})),
  973:     dets:close(TabRef),
  974:     {ok, _} = dets:open_file(TabRef, Args),
  975:     {'EXIT', {badarg, _}} =
  976: 	(catch dets:init_table(TabRef, nofun, {format,bchunk})),
  977:     dets:close(TabRef),
  978:     {ok, _} = dets:open_file(TabRef, Args),
  979:     away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end,
  980: 					{format,bchunk})),
  981:     dets:close(TabRef),
  982:     {ok, _} = dets:open_file(TabRef, Args),
  983:     {error, {init_fun, fopp}} =
  984: 	dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}),
  985:     dets:close(TabRef),
  986:     {ok, _} = dets:open_file(TabRef, Args),
  987:     dets:safe_fixtable(TabRef, true),
  988:     {error, {fixed_table, TabRef}} =
  989: 	dets:init_table(TabRef, init([]), {format,bchunk}),
  990:     dets:safe_fixtable(TabRef, false),
  991:     ok = dets:close(TabRef),
  992: 
  993:     {ok, _} = dets:open_file(TabRef, [{access,read} | Args]),
  994:     {error, {access_mode, Fname}} =
  995: 	dets:init_table(TabRef, init([]), {format,bchunk}),
  996:     ok = dets:close(TabRef),
  997: 
  998:     {ok, _} = dets:open_file(TabRef, Args),
  999:     {error, {init_fun,{1,2}}} =
 1000: 	dets:init_table(TabRef, init([[{1,2},bad,{3,4}]]), {format,bchunk}),
 1001:     _ = dets:close(TabRef),
 1002:     file:delete(Fname),
 1003: 
 1004:     {ok, _} = dets:open_file(TabRef, Args),
 1005:     {error, {init_fun, end_of_input}} =
 1006: 	dets:init_table(TabRef, init([]),{format,bchunk}),
 1007:     _ = dets:close(TabRef),
 1008:     file:delete(Fname),
 1009: 
 1010:     {ok, _} = dets:open_file(TabRef, Args),
 1011:     {'EXIT', {badarg, _}} =
 1012: 	(catch dets:init_table(TabRef, init([]),{format,foppla})),
 1013:     _ = dets:close(TabRef),
 1014:     file:delete(Fname),
 1015: 
 1016:     {ok, _} = dets:open_file(TabRef, Args),
 1017:     ok = ins(TabRef, 100),
 1018: 
 1019:     [BParms | Objs] = collect_bchunk(TabRef, init_bchunk(TabRef)),
 1020:     Parms = binary_to_term(BParms),
 1021:     {error, {init_fun, <<"foobar">>}} =
 1022: 	dets:init_table(TabRef, init([[<<"foobar">>]]),{format,bchunk}),
 1023:     _ = dets:close(TabRef),
 1024:     file:delete(Fname),
 1025: 
 1026:     {ok, _} = dets:open_file(TabRef, Args),
 1027:     Parms1 = setelement(1, Parms, foobar),
 1028:     BParms1 = term_to_binary(Parms1),
 1029:     {error, {init_fun, BParms1}} =
 1030: 	dets:init_table(TabRef, init([[BParms1 | Objs]]),{format,bchunk}),
 1031:     _ = dets:close(TabRef),
 1032:     file:delete(Fname),
 1033: 
 1034:     {ok, _} = dets:open_file(TabRef, Args),
 1035:     [{Sz1,No1} | NoColls17] = element(tuple_size(Parms), Parms),
 1036:     Parms2 = setelement(tuple_size(Parms), Parms, [{Sz1,No1+1} | NoColls17]),
 1037:     BParms2 = term_to_binary(Parms2),
 1038:     {error, invalid_objects_list} =
 1039: 	dets:init_table(TabRef, init([[BParms2 | Objs]]),{format,bchunk}),
 1040:     _ = dets:close(TabRef),
 1041:     file:delete(Fname),
 1042: 
 1043:     {ok, _} = dets:open_file(TabRef, Args),
 1044:     [{LSize1,Slot1,Obj1} | ObjsRest] = Objs,
 1045:   
 1046:     BadSize = byte_size(Obj1)-1,
 1047:     <<BadSizeObj:BadSize/binary,_:1/binary>> = Obj1,
 1048:     BadObjs = [{LSize1,Slot1,BadSizeObj} | ObjsRest],
 1049:     {error, invalid_objects_list} =
 1050: 	dets:init_table(TabRef, init([[BParms | BadObjs]]),{format,bchunk}),
 1051:     _ = dets:close(TabRef),
 1052:     file:delete(Fname),
 1053: 
 1054:     {ok, _} = dets:open_file(TabRef, Args),
 1055:     <<Size:32,BigObj0/binary>> = list_to_binary(lists:duplicate(16,Obj1)),
 1056:     BigObj = <<(Size*16):32,BigObj0/binary>>,
 1057:     BadColl = [BParms, {LSize1+4,Slot1,BigObj} | ObjsRest],
 1058:     {error, invalid_objects_list} =
 1059:          dets:init_table(TabRef, init([BadColl]),{format,bchunk}),
 1060:     _ = dets:close(TabRef),
 1061:     file:delete(Fname),
 1062: 
 1063:     {ok, _} = dets:open_file(TabRef, Args),
 1064:     BadObj = <<"foobar">>,
 1065:     {error, invalid_objects_list} =
 1066: 	dets:init_table(TabRef, init([[BParms, BadObj]]),{format,bchunk}),
 1067:     _ = dets:close(TabRef),
 1068:     file:delete(Fname),
 1069: 
 1070:     {ok, _} = dets:open_file(TabRef, [{type,bag} | Args]),
 1071:     {error, {init_fun, _}} =
 1072: 	dets:init_table(TabRef, init([[BParms]]),{format,bchunk}),
 1073:     _ = dets:close(TabRef),
 1074:     file:delete(Fname),
 1075: 
 1076:     ok = dets:close(Source),
 1077:     file:delete(SourceFname),
 1078: 
 1079:     L1 = [{1,a},{2,b},{3,c},{4,d}],
 1080:     fast_bulk_init(L1, set, 4, 4, Config, V),
 1081:     L2 = [{1,a},{2,b},{2,q},{3,c},{4,d},{4,e},{2,q}],
 1082:     fast_bulk_init(L2, set, 4, 4, Config, V),
 1083:     fast_bulk_init(L2, bag, 6, 4, Config, V),
 1084:     fast_bulk_init(L2, duplicate_bag, 7, 4, Config, V),
 1085:     fast_bulk_init(L1, set, 4, 4, 512, Config, V),
 1086:     fast_bulk_init([], set, 0, 0, 10000, Config, V),
 1087:     file:delete(Fname),
 1088: 
 1089:     %% Initiate a file that contains a lot of objects.
 1090:     {ok, _} = dets:open_file(Source, [{min_no_slots,10000} | SourceArgs]),
 1091:     Fun1 = init_fun(0, 10000),
 1092:     ok = dets:init_table(Source, Fun1, {format,term}),
 1093:     
 1094:     {ok, _} = dets:open_file(TabRef, [{min_no_slots,10000} | Args]),
 1095:     ok = ins(TabRef, 6000),
 1096:     Fun2 = init_bchunk(Source),
 1097:     true =
 1098:         dets:is_compatible_bchunk_format(TabRef, 
 1099:                                          dets:info(Source, bchunk_format)),
 1100:     false = dets:is_compatible_bchunk_format(TabRef, <<"foobar">>),
 1101:     ok = dets:init_table(TabRef, Fun2, {format, bchunk}),
 1102:     ok = dets:close(Source),
 1103:     file:delete(SourceFname),
 1104:     All = sort(get_all_objects(TabRef)),
 1105:     FAll = get_all_objects_fast(TabRef),
 1106:     true = All =:= sort(FAll),
 1107:     true = length(All) =:= 10000,
 1108:     ok = dets:close(TabRef),
 1109:     file:delete(Fname),
 1110: 
 1111:     %% Initiate inserts fewer objects than the table contains.
 1112:     {ok, _} = dets:open_file(Source, [{min_no_slots,1000} | SourceArgs]),
 1113:     ok = ins(Source, 4000),
 1114:     
 1115:     {ok, _} = dets:open_file(TabRef, [{min_no_slots,1000} | Args]),
 1116:     ok = ins(TabRef, 6000),
 1117:     FileSize1 = dets:info(TabRef, file_size),
 1118:     Fun4 = init_bchunk(Source),
 1119:     ok = dets:init_table(TabRef, Fun4, {format, bchunk}),
 1120:     ok = dets:close(Source),
 1121:     file:delete(SourceFname),
 1122:     FileSize2 = dets:info(TabRef, file_size),
 1123:     All_2 = sort(get_all_objects(TabRef)),
 1124:     FAll_2 = get_all_objects_fast(TabRef),
 1125:     true = All_2 =:= sort(FAll_2),
 1126:     true = length(All_2) =:= 4000,
 1127:     ok = dets:close(TabRef),
 1128:     true = FileSize1 > FileSize2,
 1129: 
 1130:     %% Bchunk and fixed table.
 1131:     {ok, _} = dets:open_file(TabRef, Args),
 1132:     NoItems = dets:info(TabRef, no_objects),
 1133:     AllObjects1 = sort(get_all_objects_fast(TabRef)),
 1134:     dets:safe_fixtable(TabRef, true),
 1135:     true = dets:info(TabRef, fixed),
 1136:     Cont1 = init_bchunk(TabRef),
 1137:     NoDel =
 1138: 	dets:select_delete(TabRef, [{{'_',{item,'_','_'}},[],[true]}]),
 1139:     true = (NoDel > 0),
 1140:     AllObjects2 = sort(get_all_objects_fast(TabRef)),
 1141:     true = dets:info(TabRef, fixed),
 1142:     Cont2 = init_bchunk(TabRef),
 1143:     NoItems2 = dets:info(TabRef, no_objects),
 1144:     true = (NoItems =:= NoItems2 + NoDel),
 1145:     NoDel2 = dets:select_delete(TabRef, [{'_',[],[true]}]),
 1146:     true = (NoDel2 > 0),
 1147:     AllObjects3 = sort(get_all_objects_fast(TabRef)),
 1148:     NoItems3 = dets:info(TabRef, no_objects),
 1149:     true = (NoItems3 =:= 0),
 1150:     true = dets:info(TabRef, fixed),
 1151:     true = (NoItems2 =:= NoItems3 + NoDel2),
 1152:     Cont3 = init_bchunk(TabRef),
 1153: 
 1154:     BinColl1 = collect_bchunk(TabRef, Cont1),
 1155:     BinColl2 = collect_bchunk(TabRef, Cont2),
 1156:     BinColl3 = collect_bchunk(TabRef, Cont3),
 1157:     dets:safe_fixtable(TabRef, false),
 1158:     ok = dets:close(TabRef),
 1159:     file:delete(Fname),
 1160: 
 1161:     %% Now check that the above collected binaries are correct.
 1162:     {ok, _} = dets:open_file(TabRef, Args),
 1163:     ok = dets:init_table(TabRef, init([BinColl1]),{format,bchunk}),
 1164:     true = (AllObjects1 =:= sort(get_all_objects_fast(TabRef))),
 1165:     true = (length(AllObjects1) =:= dets:info(TabRef, no_objects)),
 1166:     ok = dets:init_table(TabRef, init([BinColl2]),{format,bchunk}),
 1167:     true = (AllObjects2 =:= sort(get_all_objects_fast(TabRef))),
 1168:     true = (length(AllObjects2) =:= dets:info(TabRef, no_objects)),
 1169:     ok = dets:init_table(TabRef, init([BinColl3]),{format,bchunk}),
 1170:     true = (AllObjects3 =:= sort(get_all_objects_fast(TabRef))),
 1171:     true = (length(AllObjects3) =:= dets:info(TabRef, no_objects)),
 1172:     ok = dets:close(TabRef),
 1173:     file:delete(Fname),
 1174:     check_pps(P0),
 1175:     ok.
 1176: 
 1177: fast_bulk_init(L, Type, N, NoKeys, Config, V) ->
 1178:     fast_bulk_init(L, Type, N, NoKeys, 256, Config, V).
 1179: 
 1180: fast_bulk_init(L, Type, N, NoKeys, Est, Config, V) ->
 1181:     T = init_table_test,
 1182:     Fname = filename(T, Config),
 1183:     file:delete(Fname),
 1184: 
 1185:     Args0 = [{ram_file,false}, {type,Type},{keypos,1},
 1186: 	    {estimated_no_objects, Est},{version,V}],
 1187:     Args = [{file,Fname} | Args0],
 1188:     S = init_table_test_source,
 1189:     SFname = filename(S, Config),
 1190:     file:delete(SFname),
 1191:     SArgs = [{file,SFname} | Args0],
 1192: 
 1193:     {ok, S} = dets:open_file(S, SArgs),
 1194:     ok = dets:insert(S, L),
 1195: 
 1196:     Input = init_bchunk(S),
 1197:     {ok, T} = dets:open_file(T, Args),
 1198:     ok = dets:init_table(T, Input, [{format,bchunk}]),
 1199:     All = sort(get_all_objects(T)),
 1200:     FAll = get_all_objects_fast(T),
 1201:     true = All =:= sort(FAll),
 1202:     true = length(All) =:= N,
 1203:     true = dets:info(T, size) =:= N,
 1204:     true = dets:info(T, no_keys) =:= NoKeys,
 1205:     ok = dets:close(T),
 1206:     
 1207:     {ok, T} = dets:open_file(T, Args),
 1208:     All2 = sort(get_all_objects(T)),
 1209:     FAll2 = get_all_objects_fast(T),
 1210:     true = All =:= All2,
 1211:     true = All =:= sort(FAll2),
 1212:     ok = dets:close(T),
 1213:     file:delete(Fname),
 1214: 
 1215:     ok = dets:close(S),
 1216:     file:delete(SFname),
 1217:     ok.
 1218: 
 1219: init_bchunk(T) ->
 1220:     Start = dets:bchunk(T, start),
 1221:     init_bchunk(T, Start).
 1222: 
 1223: init_bchunk(Tab, State) ->
 1224:     fun(read) when State =:= '$end_of_table' ->
 1225: 	    end_of_input;
 1226:        (read) when element(1, State) =:= error ->
 1227: 	    State;
 1228:        (read) ->
 1229: 	    {Cont, Objs} = State,
 1230: 	    {Objs, init_bchunk(Tab, dets:bchunk(Tab, Cont))};
 1231:        (close) ->
 1232: 	    ok
 1233:     end.
 1234: 
 1235: collect_bchunk(Tab, Fun) ->
 1236:     collect_bchunk(Tab, Fun, []).
 1237: 
 1238: collect_bchunk(Tab, Fun, L) ->
 1239:     case Fun(read) of
 1240: 	end_of_input ->
 1241: 	    lists:append(lists:reverse(L));
 1242: 	{Objs, Fun2} when is_list(Objs) ->
 1243: 	    collect_bchunk(Tab, Fun2, [Objs | L]);
 1244: 	Error ->
 1245: 	    Error
 1246:     end.
 1247: 
 1248: items(I, N, C, L) when I =:= N; C =:= 0 ->
 1249:     {I, L};
 1250: items(I, N, C, L) ->
 1251:     items(I+1, N, C-1, [{I, item(I)} | L]).
 1252: 
 1253: repair_v8(doc) ->
 1254:     ["open_file and repair."];
 1255: repair_v8(suite) -> 
 1256:     [];
 1257: repair_v8(Config) when is_list(Config) ->
 1258:     repair(Config, 8).
 1259: 
 1260: repair_v9(doc) ->
 1261:     ["open_file and repair."];
 1262: repair_v9(suite) -> 
 1263:     [];
 1264: repair_v9(Config) when is_list(Config) ->
 1265:     %% Convert from format 9 to format 8.
 1266:     T = convert_98,
 1267:     Fname = filename(T, Config),
 1268:     file:delete(Fname),
 1269:     {ok, _} = dets:open_file(T, [{file,Fname},{version,9},
 1270: 				       {type,duplicate_bag}]),
 1271:     9 = dets:info(T, version),
 1272:     true = is_binary(dets:info(T, bchunk_format)),
 1273:     ok = dets:insert(T, [{1,a},{2,b},{1,c},{2,c},{1,c},{2,a},{1,b}]),
 1274:     dets:close(T),
 1275:     {error, {version_mismatch, _}} =
 1276: 	dets:open_file(T, [{file,Fname},{version,8},{type,duplicate_bag}]),
 1277:     {ok, _} = dets:open_file(T, [{file,Fname},{version,8},
 1278: 				       {type,duplicate_bag},{repair,force}]),
 1279:     8 = dets:info(T, version),
 1280:     true = undefined =:= dets:info(T, bchunk_format),
 1281:     [{1,a},{1,b},{1,c},{1,c}] = sort(dets:lookup(T, 1)),
 1282:     [{2,a},{2,b},{2,c}] = sort(dets:lookup(T, 2)),
 1283:     7 = dets:info(T, no_objects),
 1284:     no_keys_test(T),
 1285:     _ = histogram(T, silent),
 1286:     ok = dets:close(T),
 1287:     file:delete(Fname),
 1288: 
 1289:     %% The short lived format 9(a).
 1290:     %% Not very throughly tested here.
 1291:     A9 = a9,
 1292:     Version9aS = filename:join(?datadir(Config), "version_9a.dets"),
 1293:     Version9aT = filename('v9a.dets', Config),
 1294:     {ok, _} = file:copy(Version9aS, Version9aT),
 1295:     {ok, A9} = dets:open_file(A9, [{file,Version9aT}]),
 1296:     undefined = dets:info(A9, bchunk_format),
 1297:     [{1,a},{2,b},{3,c}] = sort(dets:match_object(A9, '_')),
 1298:     ok = dets:insert(A9, {4,d}),
 1299:     ok = dets:close(A9),
 1300:     {ok, A9} = dets:open_file(A9, [{file,Version9aT}]),
 1301:     {error, old_version} = dets:bchunk(A9, start),
 1302:     ok = dets:close(A9),
 1303:     io:format("Expect forced repair:~n"),
 1304:     {ok, A9} = dets:open_file(A9, [{file,Version9aT},{repair,force}]),
 1305:     {_, _} = dets:bchunk(A9, start),
 1306:     ok = dets:close(A9),
 1307:     file:delete(Version9aT),
 1308: 
 1309:     repair(Config, 9).
 1310: 
 1311: repair(Config, V) ->
 1312:     TabRef = repair_test,
 1313:     Fname = filename(TabRef, Config),
 1314:     file:delete(Fname),
 1315:     HeadSize = headsz(V),
 1316: 
 1317:     P0 = pps(),
 1318:     {'EXIT', {badarg, _}} =
 1319: 	(catch dets:open_file(TabRef, [{min_no_slots,1000},
 1320: 				       {max_no_slots,500}])),
 1321:     {error,{file_error,hoppla,enoent}} = dets:file_info(hoppla),
 1322:     {error,{file_error,Fname,enoent}} =
 1323: 	dets:open_file(TabRef, [{file, Fname}, {access, read}]),
 1324: 
 1325:     %% compacting, and some kind of test that free lists are saved OK on file
 1326:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1327:     0 = dets:info(TabRef, size),
 1328:     ok = ins(TabRef, 30000),
 1329:     ok = del(TabRef, 30000, 3),
 1330:     ok = dets:close(TabRef),
 1331:     {error, {access_mode,Fname}} =
 1332:         dets:open_file(foo, [{file,Fname},{repair,force},{access,read}]),
 1333:     {ok, Ref3} = dets:open_file(Fname), % no repair!
 1334:     20000 = dets:info(Ref3, size),
 1335:     20000 = dets:foldl(fun(_, N) -> N+1 end, 0, Ref3),
 1336:     20000 = count_objects_quite_fast(Ref3), % actually a test of match
 1337:     no_keys_test(Ref3),
 1338:     ok = dets:close(Ref3),
 1339:     if
 1340:         V =:= 8 ->
 1341:             {ok, TabRef} = dets:open_file(TabRef,
 1342:                                    [{file, Fname},{version,V},{access,read}]),
 1343:             ok = dets:close(TabRef),
 1344:             io:format("Expect compacting repair:~n"),
 1345:             {ok, TabRef} = dets:open_file(TabRef,
 1346:                                                 [{file, Fname},{version,V}]),
 1347:             20000 = dets:info(TabRef, size),
 1348: 	    _ = histogram(TabRef, silent),
 1349:             ok = dets:close(TabRef);
 1350:         true ->
 1351:             ok
 1352:     end,
 1353:     {error,{keypos_mismatch,Fname}} =
 1354: 	dets:open_file(TabRef, [{file, Fname},{keypos,17}]),
 1355:     {error,{type_mismatch,Fname}} =
 1356: 	dets:open_file(TabRef, [{file, Fname},{type,duplicate_bag}]),
 1357: 
 1358:     %% make one of the temporary files unwritable
 1359:     TmpFile = if 
 1360: 		  V =:= 8 -> 
 1361: 		      Fname ++ ".TMP.10000"; 
 1362: 		  true -> Fname ++ ".TMP.1" 
 1363: 	      end,
 1364:     file:delete(TmpFile),
 1365:     {ok, TmpFd} = file:open(TmpFile, [read,write]),
 1366:     ok = file:close(TmpFd),
 1367:     unwritable(TmpFile),
 1368:     {error,{file_error,TmpFile,eacces}} = dets:fsck(Fname, V),
 1369:     {ok, _} = dets:open_file(TabRef,
 1370:                              [{repair,false},{file, Fname},{version,V}]),
 1371:     20000 = length(get_all_objects(TabRef)),
 1372:     _ = histogram(TabRef, silent),
 1373:     20000 = length(get_all_objects_fast(TabRef)),
 1374:     ok = dets:close(TabRef),
 1375:     writable(TmpFile),
 1376:     file:delete(TmpFile),
 1377: 
 1378:     truncate(Fname, HeadSize + 10),
 1379:     {error,{not_closed, Fname}} =
 1380: 	dets:open_file(TabRef, [{file, Fname}, {access, read}]),
 1381:     {error,{not_closed, Fname}} =
 1382: 	dets:open_file(TabRef, [{file, Fname}, {access, read}, 
 1383:                                 {repair,force}]),
 1384:     {error,{needs_repair, Fname}} =
 1385: 	dets:open_file(TabRef, [{file, Fname}, {repair, false}]),
 1386:     file:delete(Fname),
 1387: 
 1388:     %% truncated file header
 1389:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1390:     ok = ins(TabRef, 100),
 1391:     ok = dets:close(TabRef),
 1392:     truncate(Fname, HeadSize - 10),
 1393:     %% a new file is created ('tooshort')
 1394:     {ok, TabRef} = dets:open_file(TabRef,
 1395:                                   [{file,Fname},{version,V},
 1396:                                    {min_no_slots,1000},
 1397:                                    {max_no_slots,1000000}]),
 1398:     case dets:info(TabRef, no_slots) of
 1399: 	undefined -> ok;
 1400: 	{Min1,Slot1,Max1} ->
 1401: 	    true = Min1 =< Slot1, true = Slot1 =< Max1,
 1402: 	    true = 1000 < Min1, true = 1000+256 > Min1,
 1403: 	    true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1
 1404:     end,
 1405:     0 = dets:info(TabRef, size),
 1406:     no_keys_test(TabRef),
 1407:     _ = histogram(TabRef, silent),
 1408:     ok = dets:close(TabRef),
 1409:     file:delete(Fname),
 1410: 
 1411:     %% version bump (v8)
 1412:     Version7S = filename:join(?datadir(Config), "version_r2d.dets"),
 1413:     Version7T = filename('v2.dets', Config),
 1414:     {ok, _} = file:copy(Version7S, Version7T),
 1415:     {error,{version_bump, Version7T}} = dets:open_file(Version7T),
 1416:     {error,{version_bump, Version7T}} =
 1417: 	dets:open_file(Version7T, [{file,Version7T},{repair,false}]),
 1418:     {error,{version_bump, Version7T}} =
 1419: 	dets:open_file(Version7T, [{file, Version7T}, {access, read}]),
 1420:     io:format("Expect upgrade:~n"),
 1421:     {ok, _} = dets:open_file(Version7T,
 1422:                                    [{file, Version7T},{version, V}]),
 1423:     [{1,a},{2,b}] = sort(get_all_objects(Version7T)),
 1424:     [{1,a},{2,b}] = sort(get_all_objects_fast(Version7T)),
 1425:     Phash = if 
 1426: 		V =:= 8 -> phash;
 1427: 		true -> phash2
 1428: 	    end,
 1429:     Phash = dets:info(Version7T, hash),
 1430:     _ = histogram(Version7T, silent),
 1431:     ok = dets:close(Version7T),
 1432:     {ok, _} = dets:open_file(Version7T, [{file, Version7T}]),
 1433:     Phash = dets:info(Version7T, hash),
 1434:     ok = dets:close(Version7T),
 1435:     file:delete(Version7T),
 1436: 
 1437:     %% converting free lists
 1438:     Version8aS = filename:join(?datadir(Config), "version_r3b02.dets"),
 1439:     Version8aT = filename('v3.dets', Config),
 1440:     {ok, _} = file:copy(Version8aS, Version8aT),
 1441:     %% min_no_slots and max_no_slots are ignored - no repair is taking place
 1442:     {ok, _} = dets:open_file(version_8a,
 1443: 				   [{file, Version8aT},{min_no_slots,1000},
 1444: 				    {max_no_slots,100000}]),
 1445:     [{1,b},{2,a},{a,1},{b,2}] = sort(get_all_objects(version_8a)),
 1446:     [{1,b},{2,a},{a,1},{b,2}] = sort(get_all_objects_fast(version_8a)),
 1447:     ok = ins(version_8a, 1000),
 1448:     1002 = dets:info(version_8a, size),
 1449:     no_keys_test(version_8a),
 1450:     All8a = sort(get_all_objects(version_8a)),
 1451:     1002 = length(All8a),
 1452:     FAll8a = sort(get_all_objects_fast(version_8a)),
 1453:     true = sort(All8a) =:= sort(FAll8a),
 1454:     ok = del(version_8a, 300, 3),
 1455:     902 = dets:info(version_8a, size),
 1456:     no_keys_test(version_8a),
 1457:     All8a2 = sort(get_all_objects(version_8a)),
 1458:     902 = length(All8a2),
 1459:     FAll8a2 = sort(get_all_objects_fast(version_8a)),
 1460:     true = sort(All8a2) =:= sort(FAll8a2),
 1461:     _ = histogram(version_8a, silent),
 1462:     ok = dets:close(version_8a),
 1463:     file:delete(Version8aT),
 1464: 
 1465:     %% will fail unless the slots are properly sorted when repairing (v8)
 1466:     BArgs = [{file, Fname},{type,duplicate_bag},
 1467: 	     {delayed_write,{3000,10000}},{version,V}],
 1468:     {ok, TabRef} = dets:open_file(TabRef, BArgs),
 1469:     Seq = seq(1, 500),
 1470:     Small = map(fun(X) -> {X,X} end, Seq),
 1471:     Big = map(fun(X) -> erlang:make_tuple(20, X) end, Seq),
 1472:     ok = dets:insert(TabRef, Small),
 1473:     ok = dets:insert(TabRef, Big),
 1474:     ok = dets:insert(TabRef, Small),
 1475:     ok = dets:insert(TabRef, Big),
 1476:     All = sort(safe_get_all_objects(TabRef)),
 1477:     ok = dets:close(TabRef),
 1478:     io:format("Expect forced repair:~n"),
 1479:     {ok, _} =
 1480:          dets:open_file(TabRef, [{repair,force},{min_no_slots,2000} | BArgs]),
 1481:     if 
 1482: 	V =:= 9 ->
 1483: 	    {MinNoSlots,_,MaxNoSlots} = dets:info(TabRef, no_slots),
 1484: 	    ok = dets:close(TabRef),
 1485: 	    io:format("Expect compaction:~n"),
 1486: 	    {ok, _} =
 1487: 		dets:open_file(TabRef, [{repair,force},
 1488: 					{min_no_slots,MinNoSlots},
 1489: 					{max_no_slots,MaxNoSlots} | BArgs]);
 1490: 	true ->
 1491: 	    ok
 1492:     end,
 1493:     All2 = get_all_objects(TabRef),
 1494:     true = All =:= sort(All2),
 1495:     FAll2 = get_all_objects_fast(TabRef),
 1496:     true = All =:= sort(FAll2),
 1497:     true = length(All) =:= dets:info(TabRef, size),
 1498:     no_keys_test(TabRef),
 1499:     Fun = fun(X) -> 4 = length(dets:lookup(TabRef, X)) end,
 1500:     foreach(Fun, Seq),
 1501:     _ = histogram(TabRef, silent),
 1502:     ok = dets:close(TabRef),
 1503:     file:delete(Fname),
 1504: 
 1505:     %% object bigger than segments, the "hole" is taken care of
 1506:     {ok, TabRef} = dets:open_file(TabRef, [{file, Fname},{version,V}]),
 1507:     Tuple = erlang:make_tuple(1000, foobar), % > 2 kB
 1508:     ok = dets:insert(TabRef, Tuple),
 1509:     %% at least one full segment (objects smaller than 2 kB):
 1510:     ins(TabRef, 2000),
 1511:     ok = dets:close(TabRef),
 1512: 
 1513:     if 
 1514:         V =:= 8 ->
 1515:             %% first estimated number of objects is wrong, repair once more
 1516:             {ok, Fd} = file:open(Fname, [read,write]),
 1517:             NoPos = HeadSize - 8,  % no_objects
 1518:             file:pwrite(Fd, NoPos, <<0:32>>), % NoItems
 1519:             ok = file:close(Fd),
 1520:             dets:fsck(Fname, V),
 1521:             {ok, _} =
 1522:                 dets:open_file(TabRef, 
 1523:                                [{repair,false},{file, Fname},{version,V}]),
 1524:             2001 = length(get_all_objects(TabRef)),
 1525:             _ = histogram(TabRef, silent),
 1526:             2001 = length(get_all_objects_fast(TabRef)),
 1527:             ok = dets:close(TabRef);
 1528:         true ->
 1529:             ok
 1530:     end,
 1531: 
 1532:     {ok, _} =
 1533:         dets:open_file(TabRef, 
 1534:                        [{repair,false},{file, Fname},{version,V}]),
 1535:     {ok, ObjPos} = dets:where(TabRef, {66,{item,number,66}}),
 1536:     ok = dets:close(TabRef),
 1537:     %% Damaged object.
 1538:     Pos = 12, % v9: compaction fails, proper repair follows
 1539:     crash(Fname, ObjPos+Pos),
 1540:     io:format(
 1541: 	    "Expect forced repair (possibly after attempted compaction):~n"),
 1542:     {ok, _} =
 1543: 	dets:open_file(TabRef, [{repair,force},{file, Fname},{version,V}]),
 1544:     true = dets:info(TabRef, size) < 2001,
 1545:     ok = dets:close(TabRef),
 1546:     file:delete(Fname),
 1547: 
 1548:     %% The file is smaller than the padded object.
 1549:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1550:     ok = dets:insert(TabRef, Tuple),
 1551:     ok = dets:close(TabRef),
 1552:     io:format("Expect forced repair or compaction:~n"),
 1553:     {ok, _} =
 1554: 	dets:open_file(TabRef, [{repair,force},{file, Fname},{version,V}]),
 1555:     true = 1 =:= dets:info(TabRef, size),
 1556:     ok = dets:close(TabRef),
 1557:     file:delete(Fname),
 1558: 
 1559:     %% Damaged free lists.
 1560:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1561:     ok = ins(TabRef, 300),
 1562:     ok = dets:sync(TabRef),
 1563:     ok = del(TabRef, 300, 3),
 1564:     %% FileSize is approximately where the free lists will be written.
 1565:     FileSize = dets:info(TabRef, memory),
 1566:     ok = dets:close(TabRef),
 1567:     crash(Fname, FileSize+20),
 1568:     %% Used to return bad_freelists, but that changed in OTP-9622
 1569:     {ok, TabRef} =
 1570: 	dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1571:     ok = dets:close(TabRef),
 1572:     file:delete(Fname),
 1573: 
 1574:     %% File not closed, opening with read and read_write access tried.
 1575:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1576:     ok = ins(TabRef, 300),
 1577:     ok = dets:close(TabRef),
 1578:     crash(Fname, ?CLOSED_PROPERLY_POS+3, ?NOT_PROPERLY_CLOSED),
 1579:     {error, {not_closed, Fname}} =
 1580:        dets:open_file(foo, [{file,Fname},{version,V},{repair,force},
 1581:                             {access,read}]),
 1582:     {error, {not_closed, Fname}} =
 1583:        dets:open_file(foo, [{file,Fname},{version,V},{repair,true},
 1584:                             {access,read}]),
 1585:     io:format("Expect repair:~n"),
 1586:     {ok, TabRef} =
 1587:        dets:open_file(TabRef, [{file,Fname},{version,V},{repair,true},
 1588:                                {access,read_write}]),
 1589:     ok = dets:close(TabRef),
 1590:     crash(Fname, ?CLOSED_PROPERLY_POS+3, ?NOT_PROPERLY_CLOSED),
 1591:     io:format("Expect forced repair:~n"),
 1592:     {ok, TabRef} =
 1593:        dets:open_file(TabRef, [{file,Fname},{version,V},{repair,force},
 1594:                                {access,read_write}]),
 1595:     ok = dets:close(TabRef),
 1596:     file:delete(Fname),
 1597: 
 1598:     %% The size of an object is huge.
 1599:     {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]),
 1600:     ok = dets:insert(TabRef, [{1,2,3},{2,3,4}]),
 1601:     {ok, ObjPos2} = dets:where(TabRef, {1,2,3}),
 1602:     ok = dets:close(TabRef),
 1603:     ObjPos3 = if
 1604:                   V =:= 8 -> ObjPos2 + 4;
 1605:                   V =:= 9 -> ObjPos2
 1606:               end,
 1607:     crash(Fname, ObjPos3, 255),
 1608:     io:format("Expect forced repair:~n"),
 1609:     {ok, TabRef} =
 1610:          dets:open_file(TabRef, [{file,Fname},{version,V},{repair,force}]),
 1611:     ok = dets:close(TabRef),
 1612:     file:delete(Fname),
 1613: 
 1614:     check_pps(P0),
 1615:     ok.
 1616: 
 1617: hash_v8b_v8c(doc) ->
 1618:     ["Test the use of different hashing algorithms in v8b and v8c of the "
 1619:      "Dets file format."];
 1620: hash_v8b_v8c(suite) ->
 1621:     [];
 1622: hash_v8b_v8c(Config) when is_list(Config) ->
 1623:     Source =
 1624: 	filename:join(?datadir(Config), "dets_test_v8b.dets"),
 1625:     %% Little endian version of old file (there is an endianess bug in 
 1626:     %% the old hash). This is all about version 8 of the dets file format.
 1627: 
 1628:     P0 = pps(),
 1629:     SourceLE =
 1630: 	filename:join(?datadir(Config), 
 1631: 		      "dets_test_v8b_little_endian.dets"),
 1632:     Target1 = filename('oldhash1.dets', Config),
 1633:     Target1LE = filename('oldhash1le.dets', Config),
 1634:     Target2 = filename('oldhash2.dets', Config),
 1635:     {ok, Bin} = file:read_file(Source),
 1636:     {ok, BinLE} = file:read_file(SourceLE),
 1637:     ok = file:write_file(Target1,Bin),
 1638:     ok = file:write_file(Target1LE,BinLE),
 1639:     ok = file:write_file(Target2,Bin),
 1640:     {ok, d1} = dets:open_file(d1,[{file,Target1}]),
 1641:     {ok, d1le} = dets:open_file(d1le,[{file,Target1LE}]),
 1642:     {ok, d2} = dets:open_file(d2,[{file,Target2},{repair,force},
 1643:                                         {version,8}]),
 1644:     FF = fun(N,_F,_T) when N > 16#FFFFFFFFFFFFFFFF ->
 1645:                  ok;
 1646:             (N,F,T) ->
 1647:                  V = integer_to_list(N),
 1648:                  case dets:lookup(T,N) of
 1649:                      [{N,V}] ->
 1650:                          F(N*2,F,T);
 1651:                      _Error ->
 1652:                          exit({failed,{lookup,T,N}})
 1653:                  end
 1654:          end,
 1655:     Mess = case (catch FF(1,FF,d1)) of
 1656:                {'EXIT', {failed, {lookup,_,_}}} ->
 1657:                    ok = dets:close(d1),
 1658:                    FF(1,FF,d1le),
 1659:                    hash = dets:info(d1le,hash),
 1660:                    dets:insert(d1le,{33333333333,hejsan}),
 1661:                    [{33333333333,hejsan}] =
 1662:                        dets:lookup(d1le,33333333333),
 1663:                    ok = dets:close(d1le),
 1664:                    {ok, d1le} = dets:open_file(d1le,
 1665:                                                      [{file,Target1LE}]),
 1666:                    [{33333333333,hejsan}] =
 1667:                        dets:lookup(d1le,33333333333),
 1668:                    FF(1,FF,d1le),
 1669:                    ok = dets:close(d1le),
 1670:                    "Seems to be a little endian machine";
 1671:                {'EXIT', Fault} ->
 1672:                    exit(Fault);
 1673:                _ ->
 1674:                    ok = dets:close(d1le),
 1675:                    hash = dets:info(d1,hash),
 1676:                    dets:insert(d1,{33333333333,hejsan}),
 1677:                    [{33333333333,hejsan}] =
 1678:                        dets:lookup(d1,33333333333),
 1679:                    ok = dets:close(d1),
 1680:                    {ok, d1} = dets:open_file(d1,[{file,Target1}]),
 1681:                    [{33333333333,hejsan}] =
 1682:                        dets:lookup(d1,33333333333),
 1683:                    FF(1,FF,d1),
 1684:                    ok = dets:close(d1),
 1685:                    "Seems to be a big endian machine"
 1686:            end,
 1687:     FF(1,FF,d2),
 1688:     phash = dets:info(d2,hash),
 1689:     ok = dets:close(d2),
 1690:     file:delete(Target1),
 1691:     file:delete(Target1LE),
 1692:     file:delete(Target2),
 1693:     check_pps(P0),
 1694:     {comment, Mess}.
 1695: 
 1696: phash(doc) ->
 1697:     ["Test version 9(b) with erlang:phash/2 as hash function."];
 1698: phash(suite) ->
 1699:     [];
 1700: phash(Config) when is_list(Config) ->
 1701:     T = phash,
 1702:     Phash_v9bS = filename:join(?datadir(Config), "version_9b_phash.dat"),
 1703:     Fname = filename('v9b.dets', Config),
 1704:     {ok, _} = file:copy(Phash_v9bS, Fname),
 1705:     
 1706:     %% Deleting all objects changes the hash function. 
 1707:     %% A feature... (it's for free)
 1708:     {ok, T} = dets:open_file(T, [{file, Fname}]),
 1709:     phash = dets:info(T, hash),
 1710:     dets:delete_all_objects(T),
 1711:     phash2 = dets:info(T, hash),
 1712:     [] = get_all_objects(T),
 1713:     [] = get_all_objects_fast(T),
 1714:     ok = dets:close(T),
 1715: 
 1716:     %% The hash function is kept when compacting a table.
 1717:     {ok, _} = file:copy(Phash_v9bS, Fname),
 1718:     io:format("Expect compaction:~n"),
 1719:     {ok, T} = dets:open_file(T, [{file, Fname},{repair,force}]),
 1720:     phash = dets:info(T, hash),
 1721:     [{1,a},{2,b},{3,c},{4,d},{5,e}] =
 1722: 	lists:sort(dets:lookup_keys(T, [1,2,3,4,5])),
 1723:     ok = dets:close(T),
 1724: 
 1725:     %% The hash function is updated when repairing a table (no cost).
 1726:     {ok, _} = file:copy(Phash_v9bS, Fname),
 1727:     crash(Fname, ?CLOSED_PROPERLY_POS+3, 0),
 1728:     io:format("Expect repair:~n"),
 1729:     {ok, T} = dets:open_file(T, [{file, Fname}]),
 1730:     phash2 = dets:info(T, hash),
 1731:     [{1,a},{2,b},{3,c},{4,d},{5,e}] =
 1732: 	lists:sort(dets:lookup_keys(T, [1,2,3,4,5])),
 1733:     ok = dets:close(T),
 1734:     
 1735:     %% One cannot use the bchunk format when copying between a phash
 1736:     %% table and a phash2 table. (There is no test for the case an R9
 1737:     %% (or later) node (using phash2) copies a table to an R8 node
 1738:     %% (using phash).) See also the comment on HASH_PARMS in dets_v9.erl.
 1739:     {ok, _} = file:copy(Phash_v9bS, Fname),
 1740:     {ok, T} = dets:open_file(T, [{file, Fname}]),
 1741:     Type = dets:info(T, type),
 1742:     KeyPos = dets:info(T, keypos),
 1743:     Input = init_bchunk(T),    
 1744:     T2 = phash_table,
 1745:     Fname2 = filename(T2, Config),
 1746:     Args = [{type,Type},{keypos,KeyPos},{version,9},{file,Fname2}],
 1747:     {ok, T2} = dets:open_file(T2, Args),
 1748:     {error, {init_fun, _}} =
 1749: 	dets:init_table(T2, Input, {format,bchunk}),
 1750:     _ = dets:close(T2),
 1751:     ok = dets:close(T),
 1752:     file:delete(Fname2),
 1753: 
 1754:     file:delete(Fname),
 1755:     ok.
 1756: 
 1757: fold_v8(doc) ->
 1758:     ["foldl, foldr, to_ets"];
 1759: fold_v8(suite) ->
 1760:     [];
 1761: fold_v8(Config) when is_list(Config) ->
 1762:     fold(Config, 8).
 1763: 
 1764: fold_v9(doc) ->
 1765:     ["foldl, foldr, to_ets"];
 1766: fold_v9(suite) ->
 1767:     [];
 1768: fold_v9(Config) when is_list(Config) ->
 1769:     fold(Config, 9).
 1770: 
 1771: fold(Config, Version) ->
 1772:     T = test_table,
 1773:     N = 100,
 1774:     Fname = filename(T, Config),
 1775:     file:delete(Fname),
 1776:     P0 = pps(),
 1777: 
 1778:     Args = [{version, Version}, {file,Fname}, {estimated_no_objects, N}],
 1779:     {ok, _} = dets:open_file(T, Args),
 1780: 
 1781:     ok = ins(T, N),
 1782: 
 1783:     Ets = ets:new(to_ets, [public]),
 1784:     dets:to_ets(T, Ets),
 1785:     true = N =:= ets:info(Ets, size),
 1786:     ets:delete(Ets),
 1787: 
 1788:     Ets2 = ets:new(to_ets, [private]),
 1789:     dets:to_ets(T, Ets2),
 1790:     true = N =:= ets:info(Ets2, size),
 1791:     ets:delete(Ets2),
 1792: 
 1793:     {'EXIT', {badarg, _}} = (catch dets:to_ets(T, not_an_ets_table)),
 1794: 
 1795:     F0 = fun(X, A) -> [X | A] end,
 1796:     true = N =:= length(dets:foldl(F0, [], T)),
 1797:     true = N =:= length(dets:foldr(F0, [], T)),
 1798: 
 1799:     F1 = fun(_X, _A) -> throw(away) end, 
 1800:     away = (catch dets:foldl(F1, [], T)),
 1801:     away = (catch dets:foldr(F1, [], T)),
 1802: 
 1803:     F2 = fun(X, A) -> X + A end, 
 1804:     {'EXIT', _} = (catch dets:foldl(F2, [], T)),
 1805:     {'EXIT', _} = (catch dets:foldr(F2, [], T)),
 1806: 
 1807:     F3 = fun(_X) -> throw(away) end,
 1808:     away = (catch dets:traverse(T, F3)),
 1809: 
 1810:     F4 = fun(X) -> X + 17 end,
 1811:     {'EXIT', _} = (catch dets:traverse(T, F4)),
 1812: 
 1813:     F5 = fun(_X) -> done end,
 1814:     done = dets:traverse(T, F5),
 1815: 
 1816:     {ok, ObjPos} = dets:where(T, {66,{item,number,66}}),
 1817:     ok = dets:close(T),
 1818: 
 1819:     %% Damaged object.
 1820:     Pos = if 
 1821: 	      Version =:= 8 -> 12;
 1822: 	      Version =:= 9 -> 8
 1823: 	  end,
 1824:     crash(Fname, ObjPos+Pos),
 1825:     {ok, _} = dets:open_file(T, Args),
 1826:     io:format("Expect corrupt table:~n"),
 1827:     BadObject1 = dets:foldl(F0, [], T),
 1828:     bad_object(BadObject1, Fname),
 1829:     BadObject2 = dets:close(T),
 1830:     bad_object(BadObject2, Fname),
 1831: 
 1832:     file:delete(Fname),
 1833:     check_pps(P0),
 1834:     ok.
 1835: 
 1836: fixtable_v8(doc) ->
 1837:     ["Add objects to a fixed table."];
 1838: fixtable_v8(suite) ->
 1839:     [];
 1840: fixtable_v8(Config) when is_list(Config) ->
 1841:     fixtable(Config, 8).
 1842: 
 1843: fixtable_v9(doc) ->
 1844:     ["Add objects to a fixed table."];
 1845: fixtable_v9(suite) ->
 1846:     [];
 1847: fixtable_v9(Config) when is_list(Config) ->
 1848:     fixtable(Config, 9).
 1849: 
 1850: fixtable(Config, Version) when is_list(Config) ->
 1851:     T = fixtable,
 1852:     Fname = filename(fixtable, Config),
 1853:     file:delete(Fname),
 1854:     Args = [{version,Version},{file,Fname}],
 1855:     P0 = pps(),
 1856:     {ok, _} = dets:open_file(T, Args),
 1857: 
 1858:     %% badarg
 1859:     check_badarg(catch dets:safe_fixtable(no_table,true),
 1860: 		       dets, safe_fixtable, [no_table,true]),
 1861:     check_badarg(catch dets:safe_fixtable(T,undefined),
 1862: 		       dets, safe_fixtable, [T,undefined]),
 1863: 
 1864:     %% The table is not allowed to grow while the elements are inserted:
 1865: 
 1866:     ok = ins(T, 500),
 1867:     dets:safe_fixtable(T, false),
 1868:     %% Now the table can grow. At the same time as elements are inserted,
 1869:     %% the table tries to catch up with the previously inserted elements.
 1870:     ok = ins(T, 1000),
 1871:     1000 = dets:info(T, size),
 1872:     ok = dets:close(T),
 1873:     file:delete(Fname),
 1874: 
 1875:     {ok, _} = dets:open_file(T, [{type, duplicate_bag} | Args]),
 1876:     %% In a fixed table, delete and re-insert an object.
 1877:     ok = dets:insert(T, {1, a, b}),
 1878:     dets:safe_fixtable(T, true),
 1879:     ok = dets:match_delete(T, {1, a, b}),
 1880:     ok = dets:insert(T, {1, a, b}),
 1881:     dets:safe_fixtable(T, false),
 1882:     1 = length(dets:match_object(T, '_')),
 1883: 
 1884:     ok = dets:match_delete(T, '_'),
 1885:     %% In a fixed table, delete and insert a smaller object.
 1886:     ok = dets:insert(T, {1, duplicate(100, e)}),
 1887:     dets:safe_fixtable(T, true),
 1888:     ok = dets:match_delete(T, {1, '_'}),
 1889:     ok = dets:insert(T, {1, a, b}),
 1890:     dets:safe_fixtable(T, false),
 1891:     1 = length(dets:match_object(T, '_')),
 1892: 
 1893:     ok = dets:delete_all_objects(T),
 1894:     %% Like the last one, but one extra object.
 1895:     ok = dets:insert(T, {1, duplicate(100, e)}),
 1896:     ok = dets:insert(T, {2, duplicate(100, e)}),
 1897:     dets:safe_fixtable(T, true),
 1898:     ok = dets:match_delete(T, {1, '_'}),
 1899:     ok = dets:insert(T, {1, a, b}),
 1900:     dets:safe_fixtable(T, false),
 1901:     2 = length(dets:match_object(T, '_')),
 1902:     dets:safe_fixtable(T, true),
 1903:     ok = dets:delete_all_objects(T),
 1904:     true = dets:info(T, fixed),
 1905:     0 = length(dets:match_object(T, '_')),
 1906: 
 1907:     ok = dets:close(T),
 1908:     file:delete(Fname),
 1909:     check_pps(P0),
 1910:     ok.
 1911: 
 1912: match_v8(doc) ->
 1913:     ["Matching objects of a fixed table."];
 1914: match_v8(suite) ->
 1915:     [];
 1916: match_v8(Config) when is_list(Config) ->
 1917:     match(Config, 8).
 1918: 
 1919: match_v9(doc) ->
 1920:     ["Matching objects of a fixed table."];
 1921: match_v9(suite) ->
 1922:     [];
 1923: match_v9(Config) when is_list(Config) ->
 1924:     match(Config, 9).
 1925: 
 1926: match(Config, Version) ->
 1927:     T = match,
 1928:     Fname = filename(match, Config),
 1929:     file:delete(Fname),
 1930:     P0 = pps(),
 1931: 
 1932:     Args = [{version, Version}, {file,Fname}, {type, duplicate_bag},
 1933: 	    {estimated_no_objects,550}],
 1934:     {ok, _} = dets:open_file(T, Args),
 1935:     ok = dets:insert(T, {1, a, b}),
 1936:     ok = dets:insert(T, {1, b, a}),
 1937:     ok = dets:insert(T, {2, a, b}),
 1938:     ok = dets:insert(T, {2, b, a}),
 1939: 
 1940:     %% match, badarg
 1941:     MSpec = [{'_',[],['$_']}],
 1942:     check_badarg(catch dets:match(no_table, '_'),
 1943:                  dets, match, [no_table,'_']),
 1944:     check_badarg(catch dets:match(T, '_', not_a_number),
 1945:                  dets, match, [T,'_',not_a_number]),
 1946:     {EC1, _} = dets:select(T, MSpec, 1),
 1947:     check_badarg(catch dets:match(EC1),
 1948:                  dets, match, [EC1]),
 1949: 
 1950:     %% match_object, badarg
 1951:     check_badarg(catch dets:match_object(no_table, '_'),
 1952:                  dets, match_object, [no_table,'_']),
 1953:     check_badarg(catch dets:match_object(T, '_', not_a_number),
 1954:                  dets, match_object, [T,'_',not_a_number]),
 1955:     {EC2, _} = dets:select(T, MSpec, 1),
 1956:     check_badarg(catch dets:match_object(EC2),
 1957:                  dets, match_object, [EC2]),
 1958: 
 1959:     dets:safe_fixtable(T, true),
 1960:     {[_, _], C1} = dets:match_object(T, '_', 2),
 1961:     {[_, _], C2} = dets:match_object(C1),
 1962:     '$end_of_table' = dets:match_object(C2),
 1963:     {[_, _], C3} = dets:match_object(T, {1, '_', '_'}, 100),
 1964:     '$end_of_table' = dets:match_object(C3),
 1965:     '$end_of_table' = dets:match_object(T, {'_'}, default),
 1966:     dets:safe_fixtable(T, false),
 1967: 
 1968:     dets:safe_fixtable(T, true),
 1969:     {[_, _], C30} = dets:match(T, '$1', 2),
 1970:     {[_, _], C31} = dets:match(C30),
 1971:     '$end_of_table' = dets:match(C31),
 1972:     {[_, _], C32} = dets:match(T, {1, '$1', '_'}, 100),
 1973:     '$end_of_table' = dets:match(C32),
 1974:     '$end_of_table' = dets:match(T, {'_'}, default),
 1975:     dets:safe_fixtable(T, false),
 1976:     [[1],[1],[2],[2]] = sort(dets:match(T, {'$1','_','_'})),
 1977: 
 1978:     %% delete and insert while chunking
 1979:     %% (this case almost worthless after changes in OTP-5232)
 1980:     ok = dets:match_delete(T, '_'),
 1981:     L500 = seq(1, 500),
 1982:     Fun = fun(X) -> ok = dets:insert(T, {X, a, b, c, d}) end,
 1983:     foreach(Fun, L500),
 1984:     %% Select one object DI in L3 below to be deleted.
 1985:     {_, TmpCont} = dets:match_object(T, '_', 200),
 1986:     {_, TmpCont1} = dets:match_object(TmpCont),
 1987:     {TTL, _} = dets:match_object(TmpCont1),
 1988:     DI = if Version =:= 8 -> last(TTL); Version =:= 9 -> hd(TTL) end,
 1989:     dets:safe_fixtable(T, true),
 1990:     {L1, C20} = dets:match_object(T, '_', 200),
 1991:     true = 200 =< length(L1),
 1992:     ok = dets:match_delete(T, {'2','_','_'}), % no match
 1993:     ok = dets:match_delete(T, DI), % last object
 1994:     Tiny = {1050},
 1995:     ok = dets:insert(T, Tiny),
 1996:     true = member(Tiny, dets:match_object(T, '_')),
 1997:     {_L2, C21} = dets:match_object(C20),
 1998:     {_L3, _C22} = dets:match_object(C21),
 1999:     %% It used to be that Tiny was not visible here, but since the 
 2000:     %% scanning of files was changed to inspect the free lists every
 2001:     %% now and then it may very well be visible here.
 2002:     %% false = member(Tiny, _L3),
 2003:     %% DI used to visible here, but the above mentioned modification
 2004:     %% has changed that; it may or may not be visible.
 2005:     %% true = member(DI, _L3),
 2006:     dets:safe_fixtable(T, false),
 2007:     true = dets:member(T, 1050),
 2008:     true = member(Tiny, dets:match_object(T, '_')),
 2009:     false = member(DI, dets:match_object(T, '_')),
 2010: 
 2011:     ok = dets:close(T),
 2012:     file:delete(Fname),
 2013: 
 2014:     N = 100,
 2015:     {ok, _} = dets:open_file(T, [{estimated_no_objects,N} | Args]),
 2016:     ok = ins(T, N),
 2017:     Obj = {66,{item,number,66}},
 2018:     Spec = {'_','_'},
 2019:     {ok, ObjPos} = dets:where(T, Obj),
 2020:     ok = dets:close(T),
 2021:     %% Damaged object.
 2022:     crash(Fname, ObjPos+12),
 2023:     {ok, _} = dets:open_file(T, Args),
 2024:     io:format("Expect corrupt table:~n"),
 2025:     case ins(T, N) of
 2026: 	      ok ->
 2027:                   bad_object(dets:sync(T), Fname);
 2028: 	      Else ->
 2029: 		  bad_object(Else, Fname)
 2030: 	  end,
 2031:     io:format("Expect corrupt table:~n"),
 2032:     bad_object(dets:match(T, Spec), Fname),
 2033:     io:format("Expect corrupt table:~n"),
 2034:     bad_object(dets:match_delete(T, Spec), Fname),
 2035:     bad_object(dets:close(T), Fname),
 2036:     file:delete(Fname),
 2037: 
 2038:     {ok, _} = dets:open_file(T, [{estimated_no_objects,N} | Args]),
 2039:     ok = ins(T, N),
 2040:     {ok, ObjPos2} = dets:where(T, Obj),
 2041:     ok = dets:close(T),
 2042: 
 2043:     %% Damaged size of object.
 2044:     %% In v8, there is a next pointer before the size.
 2045:     CrashPos = if Version =:= 8 -> 5; Version =:= 9 -> 1 end,
 2046:     crash(Fname, ObjPos2+CrashPos),
 2047:     {ok, _} = dets:open_file(T, Args),
 2048:     io:format("Expect corrupt table:~n"),
 2049:     case ins(T, N) of
 2050:         ok ->
 2051:             bad_object(dets:sync(T), Fname);
 2052:         Else2 ->
 2053:             bad_object(Else2, Fname)
 2054:     end,
 2055:     %% Just echoes...
 2056:     bad_object(dets:match(T, Spec), Fname),
 2057:     bad_object(dets:match_delete(T, Spec), Fname),
 2058:     bad_object(dets:close(T), Fname),
 2059:     file:delete(Fname),
 2060: 
 2061:     {ok, _} = dets:open_file(T, [{estimated_no_objects,N} | Args]),
 2062:     ok = ins(T, N),
 2063:     {ok, ObjPos3} = dets:where(T, Obj),
 2064:     ok = dets:close(T),
 2065: 
 2066:     %% match_delete finds an error
 2067:     CrashPos3 = if Version =:= 8 -> 12; Version =:= 9 -> 16 end,
 2068:     crash(Fname, ObjPos3+CrashPos3),
 2069:     {ok, _} = dets:open_file(T, Args),
 2070:     bad_object(dets:match_delete(T, Spec), Fname),
 2071:     bad_object(dets:close(T), Fname),
 2072:     file:delete(Fname),
 2073: 
 2074:     %% The key is not fixed, but not all objects with the key are removed.
 2075:     {ok, _} = dets:open_file(T, Args),
 2076:     ok = dets:insert(T, [{1,a},{1,b},{1,c},{1,a},{1,b},{1,c}]),
 2077:     6 = dets:info(T, size),
 2078:     ok = dets:match_delete(T, {'_',a}),
 2079:     4 = dets:info(T, size),
 2080:     [{1,b},{1,b},{1,c},{1,c}] =
 2081: 	sort(dets:match_object(T,{'_','_'})),
 2082:     ok = dets:close(T),
 2083:     file:delete(Fname),
 2084: 
 2085:     check_pps(P0),
 2086:     ok.
 2087: 
 2088: select_v8(doc) ->
 2089:     ["Selecting objects of a fixed table."];
 2090: select_v8(suite) ->
 2091:     [];
 2092: select_v8(Config) when is_list(Config) ->
 2093:     select(Config, 8).
 2094: 
 2095: select_v9(doc) ->
 2096:     ["Selecting objects of a fixed table."];
 2097: select_v9(suite) ->
 2098:     [];
 2099: select_v9(Config) when is_list(Config) ->
 2100:     select(Config, 9).
 2101: 
 2102: select(Config, Version) ->
 2103:     T = select,
 2104:     Fname = filename(select, Config),
 2105:     file:delete(Fname),
 2106:     P0 = pps(),
 2107: 
 2108:     Args = [{version,Version}, {file,Fname}, {type, duplicate_bag},
 2109:             {estimated_no_objects,550}],
 2110:     {ok, _} = dets:open_file(T, Args),
 2111:     ok = dets:insert(T, {1, a, b}),
 2112:     ok = dets:insert(T, {1, b, a}),
 2113:     ok = dets:insert(T, {2, a, b}),
 2114:     ok = dets:insert(T, {2, b, a}),
 2115:     ok = dets:insert(T, {3, a, b}),
 2116:     ok = dets:insert(T, {3, b, a}),
 2117: 
 2118:     %% badarg
 2119:     MSpec = [{'_',[],['$_']}],
 2120:     check_badarg(catch dets:select(no_table, MSpec),
 2121:                  dets, select, [no_table,MSpec]),
 2122:     check_badarg(catch dets:select(T, <<17>>),
 2123:                  dets, select, [T,<<17>>]),
 2124:     check_badarg(catch dets:select(T, []),
 2125:                  dets, select, [T,[]]),
 2126:     check_badarg(catch dets:select(T, MSpec, not_a_number),
 2127:                  dets, select, [T,MSpec,not_a_number]),
 2128:     {EC, _} = dets:match(T, '_', 1),
 2129:     check_badarg(catch dets:select(EC),
 2130:                  dets, select, [EC]),
 2131: 
 2132:     AllSpec = [{'_',[],['$_']}],
 2133: 
 2134:     dets:safe_fixtable(T, true),
 2135:     {[_, _], C1} = dets:select(T, AllSpec, 2),
 2136:     {[_, _], C2} = dets:select(C1),
 2137:     {[_, _], C2a} = dets:select(C2),
 2138:     '$end_of_table' = dets:select(C2a),
 2139:     {[_, _], C3} = dets:select(T, [{{1,'_','_'},[],['$_']}], 100),
 2140:     '$end_of_table' = dets:select(C3),
 2141:     '$end_of_table' = dets:select(T, [{{'_'},[],['$_']}], default),
 2142:     dets:safe_fixtable(T, false),
 2143:     Sp1 = [{{1,'_','_'},[],['$_']},{{1,'_','_'},[],['$_']},
 2144: 	   {{2,'_','_'},[],['$_']}],
 2145:     [_,_,_,_] = dets:select(T, Sp1),
 2146:     Sp2 = [{{1,'_','_'},[],['$_']},{{1,'_','_'},[],['$_']},
 2147: 	   {{'_','_','_'},[],['$_']}],
 2148:     [_,_,_,_,_,_] = dets:select(T, Sp2),
 2149: 
 2150:     AllDeleteSpec = [{'_',[],[true]}],
 2151:     %% delete and insert while chunking
 2152:     %% (this case almost worthless after changes in OTP-5232)
 2153:     6 = dets:select_delete(T, AllDeleteSpec),
 2154:     L500 = seq(1, 500),
 2155:     Fun = fun(X) -> ok = dets:insert(T, {X, a, b, c, d}) end,
 2156:     foreach(Fun, L500),
 2157:     %% Select one object DI in L3 below to be deleted.
 2158:     {_, TmpCont} = dets:match_object(T, '_', 200),
 2159:     {_, TmpCont1} = dets:match_object(TmpCont),
 2160:     {TTL, _} = dets:match_object(TmpCont1),
 2161:     DI = if Version =:= 8 -> last(TTL); Version =:= 9 -> hd(TTL) end,
 2162:     dets:safe_fixtable(T, true),
 2163:     {L1, C20} = dets:select(T, AllSpec, 200),
 2164:     true = 200 =< length(L1),
 2165:     0 = dets:select_delete(T, [{{2,'_','_'},[],[true]}]),
 2166:     1 = dets:select_delete(T, [{DI,[],[true]}]), % last object
 2167:     Tiny = {1050},
 2168:     ok = dets:insert(T, Tiny),
 2169:     true = member(Tiny, dets:select(T, AllSpec)),
 2170:     {_L2, C21} = dets:select(C20),
 2171:     {_L3, _C22} = dets:select(C21),
 2172:     %% It used to be that Tiny was not visible here, but since the 
 2173:     %% scanning of files was changed to inspect the free lists every
 2174:     %% now and then it may very well be visible here.
 2175:     %% false = member(Tiny, _L3),
 2176:     %% DI used to visible here, but the above mentioned modification
 2177:     %% has changed that; it may or may not be visible.
 2178:     %% true = member(DI, _L3),
 2179:     true = dets:member(T, 1050),
 2180:     true = member(Tiny, dets:select(T, AllSpec)),
 2181:     false = member(DI, dets:select(T, AllSpec)),
 2182:     dets:safe_fixtable(T, false),
 2183:     true = dets:member(T, 1050),
 2184:     true = member(Tiny, dets:select(T, AllSpec)),
 2185:     false = member(DI, dets:select(T, AllSpec)),
 2186:     ok = dets:close(T),
 2187:     file:delete(Fname),
 2188: 
 2189:     %% The key is not fixed, but not all objects with the key are removed.
 2190:     {ok, _} = dets:open_file(T, Args),
 2191:     ok = dets:insert(T, [{1,a},{1,b},{1,c},{1,a},{1,b},{1,c}]),
 2192:     6 = dets:info(T, size),
 2193:     2 = dets:select_delete(T, [{{'_',a},[],[true]}]),
 2194:     4 = dets:info(T, size),
 2195:     [{1,b},{1,b},{1,c},{1,c}] = sort(dets:select(T, AllSpec)),
 2196:     ok = dets:close(T),
 2197:     file:delete(Fname),
 2198: 
 2199:     check_pps(P0),
 2200:     ok.
 2201: 
 2202: update_counter(doc) ->
 2203:     ["Test update_counter/1."];
 2204: update_counter(suite) ->
 2205:     [];
 2206: update_counter(Config) when is_list(Config) ->
 2207:     T = update_counter,
 2208:     Fname = filename(select, Config),
 2209:     file:delete(Fname),
 2210:     P0 = pps(),
 2211: 
 2212:     check_badarg(catch dets:update_counter(no_table, 1, 1),
 2213:                  dets, update_counter, [no_table,1,1]),
 2214: 
 2215:     Args = [{file,Fname},{keypos,2}],
 2216:     {ok, _} = dets:open_file(T, [{type,set} | Args]),
 2217:     {'EXIT', {badarg, _}} = (catch dets:update_counter(T, 1, 1)),
 2218:     ok = dets:insert(T, {1,a}),
 2219:     {'EXIT', {badarg, _}} = (catch dets:update_counter(T, 1, 1)),
 2220:     ok = dets:insert(T, {0,1}),
 2221:     {'EXIT', {badarg, _}} = (catch dets:update_counter(T, 1, 1)),
 2222:     ok = dets:insert(T, {0,1,0}),
 2223:     1 = dets:update_counter(T, 1, 1),
 2224:     2 = dets:update_counter(T, 1, 1),
 2225:     6 = dets:update_counter(T, 1, {3,4}),
 2226:     {'EXIT', {badarg, _}} = (catch dets:update_counter(T, 1, {0,3})),
 2227:     ok = dets:close(T),
 2228:     file:delete(Fname),
 2229: 
 2230:     {ok, _} = dets:open_file(T, [{type,bag} | Args]),
 2231:     ok = dets:insert(T, {0,1,0}),
 2232:     {'EXIT', {badarg, _}} = (catch dets:update_counter(T, 1, 1)),
 2233:     ok = dets:close(T),
 2234:     file:delete(Fname),
 2235:     check_pps(P0),
 2236: 
 2237:     ok.
 2238: 
 2239: badarg(doc) ->
 2240:     ["Call some functions with bad arguments."];
 2241: badarg(suite) ->
 2242:     [];
 2243: badarg(Config) when is_list(Config) ->
 2244:     T = badarg,
 2245:     Fname = filename(select, Config),
 2246:     file:delete(Fname),
 2247:     P0 = pps(),
 2248: 
 2249:     Args = [{file,Fname},{keypos,3}],
 2250:     {ok, _} = dets:open_file(T, [{type,set} | Args]),
 2251:     % dets:verbose(),
 2252: 
 2253:     %% badargs are tested in match, select and fixtable too.
 2254: 
 2255:     %% open
 2256:     check_badarg(catch dets:open_file({a,tuple},[]),
 2257:                  dets, open_file, [{a,tuple},[]]),
 2258:     check_badarg(catch dets:open_file({a,tuple}),
 2259:                  dets, open_file,[{a,tuple}]),
 2260:     check_badarg(catch dets:open_file(file,[foo]),
 2261:                  dets, open_file, [file,[foo]]),
 2262:     check_badarg(catch dets:open_file({hej,san},[{type,set}|3]),
 2263:                  dets, open_file, [{hej,san},[{type,set}|3]]),
 2264: 
 2265:     %% insert
 2266:     check_badarg(catch dets:insert(no_table, {1,2}),
 2267:                  dets, insert, [no_table,{1,2}]),
 2268:     check_badarg(catch dets:insert(no_table, [{1,2}]),
 2269:                  dets, insert, [no_table,[{1,2}]]),
 2270:     check_badarg(catch dets:insert(T, {1,2}),
 2271:                  dets, insert, [T,{1,2}]),
 2272:     check_badarg(catch dets:insert(T, [{1,2}]),
 2273:                  dets, insert, [T,[{1,2}]]),
 2274:     check_badarg(catch dets:insert(T, [{1,2,3} | 3]),
 2275:                  dets, insert, [T,[{1,2,3}|3]]),
 2276: 
 2277:     %% lookup{_keys}
 2278:     check_badarg(catch dets:lookup_keys(T, []),
 2279:                  dets, lookup_keys, [badarg,[]]),
 2280:     check_badarg(catch dets:lookup(no_table, 1),
 2281:                  dets, lookup, [no_table,1]),
 2282:     check_badarg(catch dets:lookup_keys(T, [1 | 2]),
 2283:                  dets, lookup_keys, [T,[1|2]]),
 2284: 
 2285:     %% member
 2286:     check_badarg(catch dets:member(no_table, 1),
 2287:                  dets, member, [no_table,1]),
 2288: 
 2289:     %% sync
 2290:     check_badarg(catch dets:sync(no_table),
 2291:                  dets, sync, [no_table]),
 2292: 
 2293:     %% delete{_keys}
 2294:     check_badarg(catch dets:delete(no_table, 1),
 2295:                  dets, delete, [no_table,1]),
 2296: 
 2297:     %% delete_object
 2298:     check_badarg(catch dets:delete_object(no_table, {1,2,3}),
 2299:                  dets, delete_object, [no_table,{1,2,3}]),
 2300:     check_badarg(catch dets:delete_object(T, {1,2}),
 2301:                  dets, delete_object, [T,{1,2}]),
 2302:     check_badarg(catch dets:delete_object(no_table, [{1,2,3}]),
 2303:                  dets, delete_object, [no_table,[{1,2,3}]]),
 2304:     check_badarg(catch dets:delete_object(T, [{1,2}]),
 2305:                  dets, delete_object, [T,[{1,2}]]),
 2306:     check_badarg(catch dets:delete_object(T, [{1,2,3} | 3]),
 2307:                  dets, delete_object, [T,[{1,2,3}|3]]),
 2308: 
 2309:     %% first,next,slot
 2310:     check_badarg(catch dets:first(no_table),
 2311:                  dets, first, [no_table]),
 2312:     check_badarg(catch dets:next(no_table, 1),
 2313:                  dets, next, [no_table,1]),
 2314:     check_badarg(catch dets:slot(no_table, 0),
 2315:                  dets, slot, [no_table,0]),
 2316: 
 2317:     %% info
 2318:     undefined = dets:info(no_table),
 2319:     undefined = dets:info(no_table, foo),
 2320:     undefined = dets:info(T, foo),
 2321: 
 2322:     %% match_delete
 2323:     check_badarg(catch dets:match_delete(no_table, '_'),
 2324:                  dets, match_delete, [no_table,'_']),
 2325: 
 2326:     %% delete_all_objects
 2327:     check_badarg(catch dets:delete_all_objects(no_table),
 2328:                  dets, delete_all_objects, [no_table]),
 2329: 
 2330:     %% select_delete
 2331:     MSpec = [{'_',[],['$_']}],
 2332:     check_badarg(catch dets:select_delete(no_table, MSpec),
 2333:                  dets, select_delete, [no_table,MSpec]),
 2334:     check_badarg(catch dets:select_delete(T, <<17>>),
 2335:                  dets, select_delete, [T, <<17>>]),
 2336: 
 2337:     %% traverse, fold
 2338:     TF = fun(_) -> continue end,
 2339:     check_badarg(catch dets:traverse(no_table, TF),
 2340:                  dets, traverse, [no_table,TF]),
 2341:     FF = fun(_, A) -> A end,
 2342:     check_badarg(catch dets:foldl(FF, [], no_table),
 2343:                  dets, foldl, [FF,[],no_table]),
 2344:     check_badarg(catch dets:foldr(FF, [], no_table),
 2345:                  dets, foldl, [FF,[],no_table]),
 2346: 
 2347:     %% close
 2348:     ok = dets:close(T),
 2349:     {error, not_owner} = dets:close(T),
 2350:     {error, not_owner} = dets:close(T),
 2351: 
 2352:     %% init_table
 2353:     IF = fun(X) -> X end,
 2354:     check_badarg(catch dets:init_table(no_table, IF),
 2355:                  dets, init_table, [no_table,IF,[]]),
 2356:     check_badarg(catch dets:init_table(no_table, IF, []),
 2357:                  dets, init_table, [no_table,IF,[]]),
 2358: 
 2359:     %% from_ets
 2360:     Ets = ets:new(ets,[]),
 2361:     check_badarg(catch dets:from_ets(no_table, Ets),
 2362:                  dets, from_ets, [no_table,Ets]),
 2363:     ets:delete(Ets),
 2364: 
 2365:     {ok, T} = dets:open_file(T, Args),
 2366:     {error,incompatible_arguments} =
 2367: 	dets:open_file(T, [{type,bag} | Args]),
 2368:     ok = dets:close(T),
 2369: 
 2370:     file:delete(Fname),
 2371:     check_pps(P0),
 2372:     ok.
 2373: 
 2374: cache_sets_v8(doc) ->
 2375:     ["Test the write cache for sets."];
 2376: cache_sets_v8(suite) ->
 2377:     [];
 2378: cache_sets_v8(Config) when is_list(Config) ->
 2379:     cache_sets(Config, 8).
 2380: 
 2381: cache_sets_v9(doc) ->
 2382:     ["Test the write cache for sets."];
 2383: cache_sets_v9(suite) ->
 2384:     [];
 2385: cache_sets_v9(Config) when is_list(Config) ->
 2386:     cache_sets(Config, 9).
 2387: 
 2388: cache_sets(Config, Version) ->
 2389:     Small = 2,
 2390:     cache_sets(Config, {0,0}, false, Small, Version),
 2391:     cache_sets(Config, {0,0}, true, Small, Version),
 2392:     cache_sets(Config, {5000,5000}, false, Small, Version),
 2393:     cache_sets(Config, {5000,5000}, true, Small, Version),
 2394:     %% Objects of size greater than 2 kB.
 2395:     Big = 1200,
 2396:     cache_sets(Config, {0,0}, false, Big, Version),
 2397:     cache_sets(Config, {0,0}, true, Big, Version),
 2398:     cache_sets(Config, {5000,5000}, false, Big, Version),
 2399:     cache_sets(Config, {5000,5000}, true, Big, Version),
 2400:     ok.
 2401:     
 2402: cache_sets(Config, DelayedWrite, Extra, Sz, Version) ->
 2403:     %% Extra = bool(). Insert tuples until the tested key is not alone.
 2404:     %% Sz = integer(). Size of the inserted tuples.
 2405: 
 2406:     T = cache,
 2407:     Fname = filename(cache, Config),
 2408:     file:delete(Fname),
 2409:     P0 = pps(),
 2410: 
 2411:     {ok, _} =
 2412: 	dets:open_file(T,[{version, Version}, {file,Fname}, {type,set}, 
 2413: 			  {delayed_write, DelayedWrite}]),
 2414: 
 2415:     Dups = 1,
 2416:     {Key, OtherKeys} = 
 2417: 	if 
 2418: 	    Extra ->
 2419: 		%% Insert enough to get three keys in some slot.
 2420: 		dets:safe_fixtable(T, true),
 2421: 		insert_objs(T, 1, Sz, Dups);
 2422: 	    true ->
 2423: 		{1,[]}
 2424: 	  end,
 2425:     Tuple = erlang:make_tuple(Sz, Key),
 2426:     ok = dets:delete(T, Key),
 2427:     ok = dets:sync(T),
 2428: 
 2429:     %% The values of keys in the same slot as Key are checked.
 2430:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2431:     
 2432:     ok = dets:insert(T, Tuple),
 2433:     [Tuple] = dets:lookup(T, Key),
 2434:     true = dets:member(T, Key),
 2435:     ok = dets:insert(T, [Tuple,Tuple]),
 2436:     %% If no delay, the cache gets filled immediately, and written.
 2437:     [Tuple] = dets:lookup_keys(T, [Key,a,b,c,d,e,f]),
 2438:     true = dets:member(T, Key),
 2439: 
 2440:     %% If delay, this happens without file access.
 2441:     ok = dets:delete(T,Key),
 2442:     ok = dets:insert(T,Tuple),
 2443:     ok = dets:insert(T,Tuple),
 2444:     [Tuple] = dets:lookup(T, Key),
 2445:     true = dets:member(T, Key),
 2446:     ok = dets:sync(T),
 2447:     [Tuple] = dets:lookup(T, Key),
 2448:     true = dets:member(T, Key),
 2449: 
 2450:     %% Key's objects are is on file only, 
 2451:     %% key 'toto' in the cache (if there is one).
 2452:     ok = dets:delete(T,toto),
 2453:     ok = dets:insert(T,[{toto,b},{toto,b}]),
 2454:     true = sort([Tuple,{toto,b}]) =:=
 2455:         sort(dets:lookup_keys(T, [Key,toto])),
 2456:     true = dets:member(T, toto),
 2457: 
 2458:     ok = dets:delete(T, Key),
 2459:     ok = dets:sync(T),
 2460:     false = dets:member(T, Key),
 2461:     Size = dets:info(T, size),
 2462: 
 2463:     %% No object with the key on the file.
 2464:     %% Delete, add one object.
 2465:     Size1 = Size + 2,
 2466:     del_and_ins(key, T, Size1, Tuple, Key, 1),
 2467:     del_and_ins(object, T, Size1, Tuple, Key, 1),
 2468:     del_and_ins(both, T, Size1, Tuple, Key, 1),
 2469: 
 2470:     %% One object with the key on the file.
 2471:     %% Delete it, add one object.
 2472:     Size2 = Size + 2,
 2473:     del_and_ins(key, T, Size2, Tuple, Key, 1),
 2474:     del_and_ins(object, T, Size2, Tuple, Key, 1),
 2475:     del_and_ins(both, T, Size2, Tuple, Key, 1),
 2476: 
 2477:     %% Overwrite an old objekt with exactly the same size.
 2478:     Element = case element(2, Tuple) of
 2479: 		  255 -> 254;
 2480: 		  E -> E + 1
 2481: 	      end,
 2482:     Tuple2 = setelement(2, Tuple, Element),
 2483:     ok = dets:sync(T),
 2484:     ok = dets:insert(T, Tuple2),
 2485:     [Tuple2] = dets:lookup(T, Key),
 2486:     true = dets:member(T, Key),
 2487:     ok = dets:sync(T),
 2488:     [Tuple2] = dets:lookup(T, Key),
 2489:     true = dets:member(T, Key),
 2490: 
 2491:     ok = dets:insert(T, {3,a}),
 2492:     ok = dets:insert(T, {3,b}),
 2493:     ok = dets:delete_object(T, {3,c}),
 2494:     ok = dets:delete_object(T, {3,d}),
 2495:     [{3,b}] = dets:lookup(T, 3),
 2496: 
 2497:     ok = dets:delete(T, 3),
 2498:     ok = dets:delete_object(T, {3,c}),
 2499:     ok = dets:delete_object(T, {3,d}),
 2500:     [] = dets:lookup(T, 3),
 2501: 
 2502:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2503:     if
 2504: 	Extra ->
 2505: 	    %% Let the table grow a while, if it needs to.
 2506: 	    All1 = get_all_objects(T),
 2507: 	    dets:safe_fixtable(T, false),
 2508: 	    timer:sleep(1000),
 2509: 	    OtherValues = sort(lookup_keys(T, OtherKeys)),
 2510: 	    dets:safe_fixtable(T, true),
 2511: 	    All2 = get_all_objects(T),
 2512: 	    FAll2 = get_all_objects_fast(T),
 2513: 	    true = sort(All2) =:= sort(FAll2),
 2514:             case symdiff(All1, All2) of
 2515: 		{[],[]} ->  ok;
 2516: 		{X,Y} ->
 2517: 		    NoBad = length(X) + length(Y),
 2518: 		    test_server:fail({sets,DelayedWrite,Extra,Sz,NoBad})
 2519: 	    end;
 2520: 	true ->
 2521: 	    ok
 2522:     end,
 2523:     ok = dets:close(T),
 2524: 
 2525:     file:delete(Fname),
 2526:     check_pps(P0),
 2527:     ok.
 2528:     
 2529: cache_bags_v8(doc) ->
 2530:     ["Test the write cache for bags."];
 2531: cache_bags_v8(suite) ->
 2532:     [];
 2533: cache_bags_v8(Config) when is_list(Config) ->
 2534:     cache_bags(Config, 8).
 2535: 
 2536: cache_bags_v9(doc) ->
 2537:     ["Test the write cache for bags."];
 2538: cache_bags_v9(suite) ->
 2539:     [];
 2540: cache_bags_v9(Config) when is_list(Config) ->
 2541:     cache_bags(Config, 9).
 2542: 
 2543: cache_bags(Config, Version) ->
 2544:     Small = 2,
 2545:     cache_bags(Config, {0,0}, false, Small, Version),
 2546:     cache_bags(Config, {0,0}, true, Small, Version),
 2547:     cache_bags(Config, {5000,5000}, false, Small, Version),
 2548:     cache_bags(Config, {5000,5000}, true, Small, Version),
 2549:     %% Objects of size greater than 2 kB.
 2550:     Big = 1200,
 2551:     cache_bags(Config, {0,0}, false, Big, Version),
 2552:     cache_bags(Config, {0,0}, true, Big, Version),
 2553:     cache_bags(Config, {5000,5000}, false, Big, Version),
 2554:     cache_bags(Config, {5000,5000}, true, Big, Version),
 2555:     ok.
 2556:     
 2557: cache_bags(Config, DelayedWrite, Extra, Sz, Version) ->
 2558:     %% Extra = bool(). Insert tuples until the tested key is not alone.
 2559:     %% Sz = integer(). Size of the inserted tuples.
 2560: 
 2561:     T = cache,
 2562:     Fname = filename(cache, Config),
 2563:     file:delete(Fname),
 2564:     P0 = pps(),
 2565: 
 2566:     {ok, _} =
 2567: 	dets:open_file(T,[{version, Version}, {file,Fname}, {type,bag}, 
 2568: 			  {delayed_write, DelayedWrite}]),
 2569: 
 2570:     Dups = 1,
 2571:     {Key, OtherKeys} = 
 2572: 	if 
 2573: 	    Extra ->
 2574: 		%% Insert enough to get three keys in some slot.
 2575: 		dets:safe_fixtable(T, true),
 2576: 		insert_objs(T, 1, Sz, Dups);
 2577: 	    true ->
 2578: 		{1,[]}
 2579: 	  end,
 2580:     Tuple = erlang:make_tuple(Sz, Key),
 2581:     ok = dets:delete(T, Key),
 2582:     ok = dets:sync(T),
 2583: 
 2584:     %% The values of keys in the same slot as Key are checked.
 2585:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2586:     
 2587:     ok = dets:insert(T, Tuple),
 2588:     [Tuple] = dets:lookup(T, Key),
 2589:     true = dets:member(T, Key),
 2590:     ok = dets:insert(T, [Tuple,Tuple]),
 2591:     %% If no delay, the cache gets filled immediately, and written.
 2592:     [Tuple] = dets:lookup_keys(T, [Key,a,b,c,d,e,f]),
 2593:     true = dets:member(T, Key),
 2594: 
 2595:     %% If delay, this happens without file access. 
 2596:     %% (This is no longer true; cache lookup has been simplified.)
 2597:     ok = dets:delete(T,Key),
 2598:     ok = dets:insert(T,Tuple),
 2599:     ok = dets:insert(T,Tuple),
 2600:     [Tuple] = dets:lookup(T, Key),
 2601:     true = dets:member(T, Key),
 2602:     ok = dets:sync(T),
 2603:     [Tuple] = dets:lookup(T, Key),
 2604:     true = dets:member(T, Key),
 2605: 
 2606:     %% Key's objects are is on file only, 
 2607:     %% key toto in the cache (if there is one).
 2608:     ok = dets:delete(T,toto),
 2609:     false = dets:member(T, toto),
 2610:     ok = dets:insert(T,[{toto,b},{toto,b}]),
 2611:     true = sort([Tuple,{toto,b}]) =:=
 2612:         sort(dets:lookup_keys(T, [Key,toto])),
 2613:     true = dets:member(T, toto),
 2614: 
 2615:     ok = dets:delete(T, Key),
 2616:     ok = dets:sync(T),
 2617:     Size = dets:info(T, size),
 2618: 
 2619:     %% No object with the key on the file.
 2620:     %% Delete, add one object.
 2621:     Size1 = Size + 2,
 2622:     del_and_ins(key, T, Size1, Tuple, Key, 1),
 2623:     del_and_ins(object, T, Size1, Tuple, Key, 1),
 2624:     del_and_ins(both, T, Size1, Tuple, Key, 1),
 2625: 
 2626:     %% One object with the key on the file.
 2627:     %% Delete it, add one object.
 2628:     Size2 = Size + 2,
 2629:     del_and_ins(key, T, Size2, Tuple, Key, 1),
 2630:     del_and_ins(object, T, Size2, Tuple, Key, 1),
 2631:     del_and_ins(both, T, Size2, Tuple, Key, 1),
 2632: 
 2633:     %% Overwrite an objekt on file with the same object.
 2634:     ok = dets:insert(T, Tuple),
 2635:     ok = dets:sync(T),
 2636:     [Tuple2] = dets:lookup(T, Key),
 2637:     true = dets:member(T, Key),
 2638:     ok = dets:insert(T, Tuple),
 2639:     ok = dets:sync(T),
 2640:     [Tuple2] = dets:lookup(T, Key),
 2641:     true = dets:member(T, Key),
 2642: 
 2643:     %% A mix of insert and delete.
 2644:     ok = dets:delete(T, Key),
 2645:     ok = dets:sync(T),
 2646:     ok = dets:delete(T, Key),
 2647:     ok = dets:insert(T, {Key,foo}),
 2648:     ok = dets:insert(T, {Key,bar}),
 2649:     [{Key,bar},{Key,foo}] = sort(dets:lookup(T, Key)),
 2650:     true = dets:member(T, Key),
 2651:     ok = dets:delete_object(T, {Key,foo}),
 2652:     ok = dets:insert(T, {Key,kar}),
 2653:     [{Key,bar},{Key,kar}] = sort(dets:lookup(T, Key)),
 2654:     true = dets:member(T, Key),
 2655:     ok = dets:insert(T, [{Key,kar},{Key,kar}]),
 2656:     [{Key,bar},{Key,kar}] = sort(dets:lookup(T, Key)),
 2657:     true = dets:member(T, Key),
 2658:     ok = dets:delete_object(T, {Key,bar}),
 2659:     ok = dets:delete_object(T, {Key,kar}),
 2660:     [] = dets:lookup(T, Key),
 2661:     false = dets:member(T, Key),
 2662:     ok = dets:sync(T),
 2663:     [] = dets:lookup(T, Key),
 2664:     false = dets:member(T, Key),
 2665: 
 2666:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2667:     if
 2668: 	Extra ->
 2669: 	    %% Let the table grow for a while, if it needs to.
 2670: 	    All1 = get_all_objects(T),
 2671: 	    dets:safe_fixtable(T, false),
 2672: 	    timer:sleep(1200),
 2673: 	    OtherValues = sort(lookup_keys(T, OtherKeys)),
 2674: 	    dets:safe_fixtable(T, true),
 2675: 	    All2 = get_all_objects(T),
 2676: 	    FAll2 = get_all_objects_fast(T),
 2677: 	    true = sort(All2) =:= sort(FAll2),
 2678:             case symdiff(All1, All2) of
 2679: 		{[],[]} ->  ok;
 2680: 		{X,Y} ->
 2681: 		    NoBad = length(X) + length(Y),
 2682: 		    test_server:fail({bags,DelayedWrite,Extra,Sz,NoBad})
 2683: 	    end;
 2684: 	true ->
 2685: 	    ok
 2686:     end,
 2687:     ok = dets:close(T),
 2688:     file:delete(Fname),
 2689: 
 2690:     %% Second object of a key added and looked up simultaneously.
 2691:     R1 = {index_test,1,2,3,4},
 2692:     R2 = {index_test,2,2,13,14},
 2693:     R3 = {index_test,1,12,13,14},
 2694:     {ok, _} = dets:open_file(T,[{version,Version},{type,bag},
 2695:                                 {keypos,2},{file,Fname}]),
 2696:     ok = dets:insert(T,R1),
 2697:     ok = dets:sync(T),
 2698:     ok = dets:insert(T,R2),
 2699:     ok = dets:sync(T),
 2700:     ok = dets:insert(T,R3),
 2701:     [R1,R3] = sort(dets:lookup(T,1)),
 2702:     true = dets:member(T, 1),
 2703:     [R1,R3] = sort(dets:lookup(T,1)),
 2704:     true = dets:member(T, 1),
 2705:     ok = dets:close(T),
 2706:     file:delete(Fname),
 2707: 
 2708:     check_pps(P0),
 2709:     ok.
 2710:     
 2711: cache_duplicate_bags_v8(doc) ->
 2712:     ["Test the write cache for duplicate bags."];
 2713: cache_duplicate_bags_v8(suite) ->
 2714:     [];
 2715: cache_duplicate_bags_v8(Config) when is_list(Config) ->
 2716:     cache_duplicate_bags(Config, 8).
 2717: 
 2718: cache_duplicate_bags_v9(doc) ->
 2719:     ["Test the write cache for duplicate bags."];
 2720: cache_duplicate_bags_v9(suite) ->
 2721:     [];
 2722: cache_duplicate_bags_v9(Config) when is_list(Config) ->
 2723:     cache_duplicate_bags(Config, 9).
 2724: 
 2725: cache_duplicate_bags(Config, Version) ->
 2726:     Small = 2,
 2727:     cache_dup_bags(Config, {0,0}, false, Small, Version),
 2728:     cache_dup_bags(Config, {0,0}, true, Small, Version),
 2729:     cache_dup_bags(Config, {5000,5000}, false, Small, Version),
 2730:     cache_dup_bags(Config, {5000,5000}, true, Small, Version),
 2731:     %% Objects of size greater than 2 kB.
 2732:     Big = 1200,
 2733:     cache_dup_bags(Config, {0,0}, false, Big, Version),
 2734:     cache_dup_bags(Config, {0,0}, true, Big, Version),
 2735:     cache_dup_bags(Config, {5000,5000}, false, Big, Version),
 2736:     cache_dup_bags(Config, {5000,5000}, true, Big, Version).
 2737: 
 2738: cache_dup_bags(Config, DelayedWrite, Extra, Sz, Version) ->
 2739:     %% Extra = bool(). Insert tuples until the tested key is not alone.
 2740:     %% Sz = integer(). Size of the inserted tuples.
 2741: 
 2742:     T = cache,
 2743:     Fname = filename(cache, Config),
 2744:     file:delete(Fname),
 2745:     P0 = pps(),
 2746: 
 2747:     {ok, _} =
 2748: 	dets:open_file(T,[{version, Version}, {file,Fname}, 
 2749: 			  {type,duplicate_bag}, 
 2750: 			  {delayed_write, DelayedWrite}]),
 2751: 
 2752:     Dups = 2,
 2753:     {Key, OtherKeys} = 
 2754: 	if 
 2755: 	    Extra ->
 2756: 		%% Insert enough to get three keys in some slot.
 2757: 		dets:safe_fixtable(T, true),
 2758: 		insert_objs(T, 1, Sz, Dups);
 2759: 	    true ->
 2760: 		{1,[]}
 2761: 	  end,
 2762:     Tuple = erlang:make_tuple(Sz, Key),
 2763:     ok = dets:delete(T, Key),
 2764:     ok = dets:sync(T),
 2765:     false = dets:member(T, Key),
 2766: 
 2767:     %% The values of keys in the same slot as Key are checked.
 2768:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2769:     
 2770:     ok = dets:insert(T, Tuple),
 2771:     [Tuple] = dets:lookup(T, Key),
 2772:     true = dets:member(T, Key),
 2773:     ok = dets:insert(T, [Tuple,Tuple]),
 2774:     %% If no delay, the cache gets filled immediately, and written.
 2775:     [Tuple,Tuple,Tuple] = dets:lookup_keys(T, [Key,a,b,c,d,e,f]),
 2776:     true = dets:member(T, Key),
 2777: 
 2778:     %% If delay, this happens without file access.
 2779:     %% (This is no longer true; cache lookup has been simplified.)
 2780:     ok = dets:delete(T,Key),
 2781:     ok = dets:insert(T,Tuple),
 2782:     ok = dets:insert(T,Tuple),
 2783:     [Tuple,Tuple] = dets:lookup(T, Key),
 2784:     true = dets:member(T, Key),
 2785:     ok = dets:sync(T),
 2786:     [Tuple,Tuple] = dets:lookup(T, Key),
 2787:     true = dets:member(T, Key),
 2788: 
 2789:     %% One object in the cache, one on the file.
 2790:     ok = dets:delete(T,Key),
 2791:     ok = dets:insert(T,Tuple),
 2792:     ok = dets:sync(T),
 2793:     ok = dets:insert(T,Tuple),
 2794:     true = dets:member(T, Key), % should not read the file, but it does..
 2795: 
 2796:     %% Key's objects are is on file only, 
 2797:     %% key toto in the cache (if there is one).
 2798:     ok = dets:delete(T,toto),
 2799:     ok = dets:insert(T,[{toto,b},{toto,b}]),
 2800:     true = sort([Tuple,Tuple,{toto,b},{toto,b}]) =:=
 2801:                  sort(dets:lookup_keys(T, [Key,toto])),
 2802:     true = dets:member(T, toto),
 2803:     Size = dets:info(T, size),
 2804: 
 2805:     %% Two objects with the same key on the file.
 2806:     %% Delete them, add two objects.
 2807:     del_and_ins(key, T, Size, Tuple, Key, 2),
 2808: 
 2809:     del_and_ins(object, T, Size, Tuple, Key, 2),
 2810:     del_and_ins(both, T, Size, Tuple, Key, 2),
 2811: 
 2812:     %% Two objects with the same key on the file.
 2813:     %% Delete them, add three objects.
 2814:     del_and_ins(key, T, Size, Tuple, Key, 3),
 2815:     del_and_ins(object, T, Size, Tuple, Key, 3),
 2816:     del_and_ins(both, T, Size, Tuple, Key, 3),
 2817: 
 2818:     %% Two objects with the same key on the file.
 2819:     %% Delete them, add one object.
 2820:     del_and_ins(key, T, Size, Tuple, Key, 1),
 2821:     del_and_ins(object, T, Size, Tuple, Key, 1),
 2822:     del_and_ins(both, T, Size, Tuple, Key, 1),
 2823: 
 2824:     OtherValues = sort(lookup_keys(T, OtherKeys)),
 2825:     if
 2826: 	Extra ->
 2827: 	    %% Let the table grow for a while, if it needs to.
 2828: 	    All1 = get_all_objects(T),
 2829: 	    dets:safe_fixtable(T, false),
 2830: 	    timer:sleep(1200),
 2831: 	    OtherValues = sort(lookup_keys(T, OtherKeys)),
 2832: 	    dets:safe_fixtable(T, true),
 2833: 	    All2 = get_all_objects(T),
 2834: 	    FAll2 = get_all_objects_fast(T),
 2835: 	    true = sort(All2) =:= sort(FAll2),
 2836:             case symdiff(All1, All2) of
 2837: 		{[],[]} ->  ok;
 2838: 		{X,Y} ->
 2839: 		    NoBad = length(X) + length(Y),
 2840: 		    test_server:fail({dup_bags,DelayedWrite,Extra,Sz,NoBad})
 2841: 	    end;
 2842: 	true ->
 2843: 	    ok
 2844:     end,
 2845:     ok = dets:close(T),
 2846: 
 2847:     file:delete(Fname),
 2848:     check_pps(P0),
 2849:     ok.
 2850:     
 2851: lookup_keys(_T, []) ->
 2852:     [];
 2853: lookup_keys(T, Keys) ->
 2854:     dets:lookup_keys(T, Keys).
 2855: 
 2856: del_and_ins(W, T, Size, Obj, Key, N) ->
 2857:     case W of
 2858: 	object -> 
 2859: 	    ok = dets:delete_object(T, Obj);
 2860: 	key ->
 2861: 
 2862: 	    ok = dets:delete(T, Key);
 2863: 	both ->
 2864: 	    ok = dets:delete(T, Key),
 2865: 	    ok = dets:delete_object(T, Obj)
 2866:     end,
 2867:     Objs = duplicate(N, Obj),
 2868:     [] = dets:lookup(T, Key),
 2869:     ok = dets:insert(T, Objs),
 2870:     Objs = dets:lookup_keys(T, [snurrespratt,Key]),
 2871:     true = Size + length(Objs)-2 =:= dets:info(T, size),
 2872:     Objs = dets:lookup(T, Key).
 2873: 
 2874: 
 2875: insert_objs(T, N, Sz, Dups) ->
 2876:     Seq = seq(N,N+255),
 2877:     L0 = map(fun(I) -> erlang:make_tuple(Sz, I) end, Seq),
 2878:     L = append(duplicate(Dups, L0)),
 2879:     ok = dets:insert(T, L),
 2880:     case search_slot(T, 0) of
 2881:         false ->
 2882:             insert_objs(T, N+256, Sz, Dups);
 2883:         Keys ->
 2884:             Keys
 2885:     end.
 2886: 
 2887: search_slot(T, I) ->
 2888:     case dets:slot(T, I) of
 2889:         '$end_of_table' ->
 2890:             false;
 2891:         Objs ->
 2892:             case usort(map(fun(X) -> element(1, X) end, Objs)) of
 2893:                 [_, Key, _ | _] = Keys0 ->
 2894:                     Keys = delete(Key, Keys0),
 2895:                     {Key, Keys};
 2896:                 _ ->
 2897:                     search_slot(T, I+1)
 2898:             end
 2899:     end.
 2900: 
 2901: symdiff(L1, L2) ->
 2902:     {X, _, Y} = 
 2903: 	sofs:symmetric_partition(sofs:set(L1), sofs:set(L2)),
 2904:     {sofs:to_external(X), sofs:to_external(Y)}.
 2905: 
 2906: otp_4208(doc) ->
 2907:     ["Read only table and traversal caused crash."];
 2908: otp_4208(suite) ->
 2909:     [];
 2910: otp_4208(Config) when is_list(Config) ->
 2911:     Tab = otp_4208,
 2912:     FName = filename(Tab, Config),
 2913:     Expected = sort([{3,ghi,12},{1,abc,10},{4,jkl,13},{2,def,11}]),
 2914: 
 2915:     file:delete(FName),
 2916:     {ok, Tab} = dets:open_file(Tab, [{file,FName}]),
 2917:     ok = dets:insert(Tab, [{1,abc,10},{2,def,11},{3,ghi,12},{4,jkl,13}]),
 2918:     Expected = sort(dets:traverse(Tab, fun(X) -> {continue, X} end)),
 2919:     ok = dets:close(Tab),
 2920: 
 2921:     {ok, Tab} = dets:open_file(Tab, [{access, read},{file,FName}]),
 2922:     Expected = sort(dets:traverse(Tab, fun(X) -> {continue, X} end)),
 2923:     ok = dets:close(Tab),
 2924:     file:delete(FName),
 2925:     
 2926:     ok.
 2927: 
 2928: otp_4989(doc) ->
 2929:     ["Read only table and growth."];
 2930: otp_4989(suite) ->
 2931:     [];
 2932: otp_4989(Config) when is_list(Config) ->
 2933:     Tab = otp_4989,
 2934:     FName = filename(Tab, Config),
 2935: 
 2936:     %% Do exactly as in the error report.
 2937:     _Ets = ets:new(Tab, [named_table]),
 2938:     ets_init(Tab, 100000),
 2939:     {ok, Tab} =
 2940:         dets:open_file(Tab, [{access, read_write}, {file,FName}, {keypos,2}]),
 2941:     ok = dets:from_ets(Tab, Tab),
 2942:     ok = dets:close(Tab),
 2943:     %% Restore.
 2944:      {ok, Tab} =
 2945:         dets:open_file(Tab, [{access, read}, {keypos, 2}, {file, FName}]),
 2946:     true = ets:delete_all_objects(Tab),
 2947:     true = ets:from_dets(Tab, Tab),
 2948:     ok = dets:close(Tab),
 2949:     ets:delete(Tab),
 2950:     file:delete(FName),
 2951:     ok.
 2952: 
 2953: ets_init(_Tab, 0) ->
 2954:     ok;
 2955: ets_init(Tab, N) ->
 2956:     ets:insert(Tab, {N,N}),
 2957:     ets_init(Tab, N - 1).
 2958: 
 2959: otp_8898(doc) ->
 2960:     ["OTP-8898. Truncated Dets file."];
 2961: otp_8898(suite) ->
 2962:     [];
 2963: otp_8898(Config) when is_list(Config) ->
 2964:     Tab = otp_8898,
 2965:     FName = filename(Tab, Config),
 2966: 
 2967:     Server = self(),
 2968: 
 2969:     file:delete(FName),
 2970:     {ok, _} = dets:open_file(Tab,[{file, FName}]),
 2971:     [P1,P2,P3] = new_clients(3, Tab),
 2972: 
 2973:     Seq = [{P1,[sync]},{P2,[{lookup,1,[]}]},{P3,[{insert,{1,b}}]}],
 2974:     atomic_requests(Server, Tab, [[]], Seq),
 2975:     true = get_replies([{P1,ok},{P2,ok},{P3,ok}]),
 2976:     ok = dets:close(Tab),
 2977:     {ok, _} = dets:open_file(Tab,[{file, FName}]),
 2978:     file:delete(FName),
 2979: 
 2980:     ok.
 2981: 
 2982: otp_8899(doc) ->
 2983:     ["OTP-8899. Several clients. Updated Head was ignored."];
 2984: otp_8899(suite) ->
 2985:     [];
 2986: otp_8899(Config) when is_list(Config) ->
 2987:     Tab = many_clients,
 2988:     FName = filename(Tab, Config),
 2989: 
 2990:     Server = self(),
 2991: 
 2992:     file:delete(FName),
 2993:     {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
 2994:     [P1,P2,P3,P4] = new_clients(4, Tab),
 2995: 
 2996:     MC = [Tab],
 2997:     Seq6a = [{P1,[{insert,[{used_to_be_skipped_by,match}]},
 2998:                   {lookup,1,[{1,a}]}]},
 2999:              {P2,[{verbose,true,MC}]},
 3000:              {P3,[{lookup,1,[{1,a}]}]}, {P4,[{verbose,true,MC}]}],
 3001:     atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6a),
 3002:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3003:     [{1,a},{2,b},{3,c},{used_to_be_skipped_by,match}] =
 3004:         lists:sort(dets:match_object(Tab, '_')),
 3005:     _ = dets:close(Tab),
 3006:     file:delete(FName),
 3007: 
 3008:     ok.
 3009: 
 3010: many_clients(doc) ->
 3011:     ["Several clients accessing a table simultaneously."];
 3012: many_clients(suite) ->
 3013:     [];
 3014: many_clients(Config) when is_list(Config) ->
 3015:     Tab = many_clients,
 3016:     FName = filename(Tab, Config),
 3017: 
 3018:     Server = self(),
 3019: 
 3020:     file:delete(FName),
 3021:     P0 = pps(),
 3022:     {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
 3023:     [P1,P2,P3,P4] = new_clients(4, Tab),
 3024: 
 3025:     %% dets:init_table/2 is used for making sure that all processes
 3026:     %% start sending requests before the Dets process begins to handle
 3027:     %% them; the requests arrive "in parallel".
 3028: 
 3029:     %% Four processes accessing the same table at almost the same time.
 3030: 
 3031:     %% One key is read, updated, and read again.
 3032:     Seq1 = [{P1,[{lookup,1,[{1,a}]}]}, {P2,[{insert,{1,b}}]},
 3033: 	    {P3,[{lookup,1,[{1,b}]}]}, {P4,[{lookup,1,[{1,b}]}]}],
 3034:     atomic_requests(Server, Tab, [[{1,a}]], Seq1),
 3035:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3036: 
 3037:     %% Different keys read by different processes
 3038:     Seq2 = [{P1,[{member,1,true}]}, {P2,[{lookup,2,[{2,b}]}]},
 3039: 	    {P3,[{lookup,1,[{1,a}]}]}, {P4,[{lookup,3,[{3,c}]}]}],
 3040:     atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq2),
 3041:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3042: 
 3043:     %% Reading deleted key.
 3044:     Seq3 = [{P1,[{delete_key,2}]}, {P2,[{lookup,1,[{1,a}]}]},
 3045: 	    {P3,[{lookup,1,[{1,a}]}]}, {P4,[{member,2,false}]}],
 3046:     atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq3),
 3047:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3048: 
 3049:     %% Inserting objects.
 3050:     Seq4 = [{P1,[{insert,[{1,a},{2,b}]}]}, {P2,[{insert,[{2,c},{3,a}]}]},
 3051: 	    {P3,[{insert,[{3,b},{4,d}]}]},
 3052: 	    {P4,[{lookup_keys,[1,2,3,4],[{1,a},{2,c},{3,b},{4,d}]}]}],
 3053:     atomic_requests(Server, Tab, [], Seq4),
 3054:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3055: 
 3056:     %% Deleting objects.
 3057:     Seq5 = [{P1,[{delete_object,{1,a}}]}, {P2,[{delete_object,{1,a}}]},
 3058: 	    {P3,[{delete_object,{3,c}}]},
 3059: 	    {P4,[{lookup_keys,[1,2,3,4],[{2,b}]}]}],
 3060:     atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq5),
 3061:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3062: 
 3063:     %% Some request not streamed.
 3064:     Seq6 = [{P1,[{lookup,1,[{1,a}]}]}, {P2,[{info,size,3}]},
 3065: 	    {P3,[{lookup,1,[{1,a}]}]}, {P4,[{info,size,3}]}],
 3066:     atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6),
 3067:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3068: 
 3069:     %% Some request not streamed.
 3070:     Seq7 = [{P1,[{insert,[{3,a}]}]}, {P2,[{insert,[{3,b}]}]},
 3071: 	    {P3,[{delete_object,{3,c}}]},
 3072: 	    {P4,[{lookup,3,[{3,b}]}]}],
 3073:     atomic_requests(Server, Tab, [[{3,c}]], Seq7),
 3074:     true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
 3075: 
 3076:     put_requests(Server, [{P1,stop},{P2,stop},{P3,stop},{P4,stop}]),
 3077:     ok = dets:close(Tab),
 3078:     file:delete(FName),
 3079: 
 3080:     %% Check that errors are handled correctly by the streaming operators.
 3081:     {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
 3082:     ok = ins(Tab, 100),
 3083:     Obj = {66,{item,number,66}},
 3084:     {ok, ObjPos} = dets:where(Tab, Obj),
 3085:     ok = dets:close(Tab),
 3086:     %% Damaged object.
 3087:     crash(FName, ObjPos+12),
 3088:     {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
 3089:     BadObject1 = dets:lookup_keys(Tab, [65,66,67,68,69]),
 3090:     bad_object(BadObject1, FName),
 3091:     _Error = dets:close(Tab),
 3092:     file:delete(FName),
 3093: 
 3094:     check_pps(P0),
 3095: 
 3096:     ok.
 3097: 
 3098: %% Tab is initiated with the objects in Objs. Objs = [[object()]].
 3099: atomic_requests(Server, Tab, Objs, Req) ->
 3100:     ok = dets:init_table(Tab, atomic_requests(Server, Objs, Req)).
 3101: 
 3102: atomic_requests(Server, L, Req) ->
 3103:     fun(close) ->
 3104: 	    ok;
 3105:        (read) when [] =:= L ->
 3106: 	    put_requests(Server, Req),
 3107: 	    end_of_input;
 3108:        (read) ->
 3109: 	    [E | Es] = L,
 3110: 	    {E, atomic_requests(Server, Es, Req)}
 3111:     end.
 3112: 
 3113: put_requests(Server, L) ->
 3114:     lists:foreach(fun({Pid,R}) -> Pid ! {Server,R}, timer:sleep(1) end, L).
 3115: 
 3116: get_replies(L) ->
 3117:     lists:all(fun({Pid,Reply}) -> Reply =:= get_reply(Pid) end, L).
 3118: 
 3119: get_reply(Pid) ->
 3120:     receive {Pid, Reply} -> Reply end.
 3121: 
 3122: new_clients(0, _Tab) ->
 3123:     [];
 3124: new_clients(N, Tab) ->
 3125:     [new_client(Tab) | new_clients(N-1, Tab)].
 3126: 
 3127: new_client(Tab) ->
 3128:     spawn(?MODULE, client, [self(), Tab]).
 3129: 
 3130: client(S, Tab) ->
 3131:     receive 
 3132: 	{S, stop} ->
 3133: 	    exit(normal);
 3134: 	{S, ToDo} ->
 3135: 	    Reply = eval(ToDo, Tab),
 3136: 	    case Reply of
 3137: 		{error, _} -> io:format("~p: ~p~n", [self(), Reply]);
 3138: 		_ -> ok
 3139: 	    end,
 3140: 	    S ! {self(), Reply}
 3141:     end,
 3142:     client(S, Tab).
 3143: 
 3144: eval([], _Tab) ->
 3145:     ok;
 3146: eval([{verbose,Bool,Expected} | L], Tab) ->
 3147:     case dets:verbose(Bool) of
 3148:         Expected -> eval(L, Tab);
 3149:         Error -> {error, {verbose,Error}}
 3150:     end;
 3151: eval([sync | L], Tab) ->
 3152:     case dets:sync(Tab) of
 3153:         ok -> eval(L, Tab);
 3154:         Error -> {error, {sync,Error}}
 3155:     end;
 3156: eval([{insert,Stuff} | L], Tab) ->
 3157:     case dets:insert(Tab, Stuff) of
 3158:         ok -> eval(L, Tab);
 3159:         Error -> {error, {insert,Stuff,Error}}
 3160:     end;
 3161: eval([{lookup,Key,Expected} | L], Tab) ->
 3162:     case dets:lookup(Tab, Key) of
 3163:         Expected -> eval(L, Tab);
 3164:         Else -> {error, {lookup,Key,Expected,Else}}
 3165:     end;
 3166: eval([{lookup_keys,Keys,Expected} | L], Tab) ->
 3167:     %% Time order is destroyed...
 3168:     case dets:lookup_keys(Tab, Keys) of
 3169:         R when is_list(R) ->
 3170:             case lists:sort(Expected) =:= lists:sort(R) of
 3171:                 true -> eval(L, Tab);
 3172:                 false -> {error, {lookup_keys,Keys,Expected,R}}
 3173:             end;
 3174:         Else -> {error, {lookup_keys,Keys,Expected,Else}}
 3175:     end;
 3176: eval([{member,Key,Expected} | L], Tab) ->
 3177:     case dets:member(Tab, Key) of
 3178:         Expected -> eval(L, Tab);
 3179:         Else -> {error, {member,Key,Expected,Else}}
 3180:     end;
 3181: eval([{delete_key,Key} | L], Tab) ->
 3182:     case dets:delete(Tab, Key) of
 3183:         ok -> eval(L, Tab);
 3184:         Else -> {error, {delete_key,Key,Else}}
 3185:     end;
 3186: eval([{delete_object,Object} | L], Tab) ->
 3187:     case dets:delete_object(Tab, Object) of
 3188:         ok -> eval(L, Tab);
 3189:         Else -> {error, {delete_object,Object,Else}}
 3190:     end;
 3191: eval([{info,Tag,Expected} | L], Tab) ->
 3192:     case dets:info(Tab, Tag) of
 3193:         Expected -> eval(L, Tab);
 3194:         Else -> {error, {info,Tag,Else,Expected}}
 3195:     end;
 3196: eval(Else, _Tab) ->
 3197:     {error, {bad_request,Else}}.
 3198: 
 3199: otp_4906(doc) ->
 3200:     ["More than 128k keys caused crash."];
 3201: otp_4906(suite) ->
 3202:     [];
 3203: otp_4906(Config) when is_list(Config) ->
 3204:     N = 256*512 + 400,
 3205:     Tab = otp_4906,
 3206:     FName = filename(Tab, Config),
 3207:     
 3208:     file:delete(FName),
 3209:     {ok, Tab} = dets:open_file(Tab, [{file, FName}]),
 3210:     ok = ins_small(Tab, 0, N),
 3211:     ok = dets:close(Tab),
 3212:     {ok, Tab} = dets:open_file(Tab, [{file, FName}]),
 3213:     ok = read_4906(Tab, N-1),
 3214:     ok = dets:close(Tab),
 3215:     file:delete(FName),
 3216: 
 3217:     %% If the (only) process fixing a table updates the table, the
 3218:     %% process will no longer be punished with a 1 ms delay (hm, the
 3219:     %% server is delayed, it should be the client...). In this example
 3220:     %% the writing process *is* delayed.
 3221:     {ok,Tab} = dets:open_file(Tab, [{file,FName}]),
 3222:     Parent = self(),
 3223:     FixPid = spawn_link(fun() -> 
 3224:                                 dets:safe_fixtable(Tab, true),
 3225:                                 receive {Parent, stop} -> ok end
 3226:                         end),
 3227:     ok = ins_small(Tab, 0, 1000),
 3228:     FixPid ! {Parent, stop},
 3229:     timer:sleep(1),
 3230:     ok = dets:close(Tab),
 3231:     file:delete(FName),
 3232:     ok.
 3233: 
 3234: read_4906(_T, N) when N < 0 ->
 3235:     ok;
 3236: read_4906(T, N) ->
 3237:     [_] = dets:lookup(T, N),
 3238:     read_4906(T, N-1).
 3239: 
 3240: ins_small(_T, I, N) when I =:= N ->
 3241:     ok;
 3242: ins_small(T, I, N) ->
 3243:     ok = dets:insert(T, {I}),
 3244:     ins_small(T, I+1, N).
 3245: 
 3246: otp_5402(doc) ->
 3247:     ["Unwritable ramfile caused krasch."];
 3248: otp_5402(suite) ->
 3249:     [];
 3250: otp_5402(Config) when is_list(Config) ->
 3251:     Tab = otp_5402,
 3252:     File = filename:join(["cannot", "write", "this", "file"]),
 3253: 
 3254:     %% close
 3255:     {ok, T} = dets:open_file(Tab, [{ram_file,true},
 3256:                                    {file, File}]),
 3257:     ok = dets:insert(T, {1,a}),
 3258:     {error,{file_error,_,_}} = dets:close(T),
 3259: 
 3260:     %% sync
 3261:     {ok, T} = dets:open_file(Tab, [{ram_file,true},
 3262:                                    {file, File}]),
 3263:     ok = dets:insert(T, {1,a}),
 3264:     {error,{file_error,_,_}} = dets:sync(T),
 3265:     {error,{file_error,_,_}} = dets:close(T),
 3266: 
 3267:     %% auto_save
 3268:     {ok, T} = dets:open_file(Tab, [{ram_file,true},
 3269:                                    {auto_save, 2000},
 3270:                                    {file, File}]),
 3271:     ok = dets:insert(T, {1,a}),
 3272:     timer:sleep(5000),
 3273:     {error,{file_error,_,_}} = dets:close(T),
 3274:     ok.
 3275: 
 3276: simultaneous_open(doc) ->
 3277:     ["Several clients open and close tables simultaneously."];
 3278: simultaneous_open(suite) ->
 3279:     [];
 3280: simultaneous_open(Config) ->
 3281:     Tab = sim_open,
 3282:     File = filename(Tab, Config),
 3283:     
 3284:     ok = monit(Tab, File),
 3285:     ok = kill_while_repairing(Tab, File),
 3286:     ok = kill_while_init(Tab, File),
 3287:     ok = open_ro(Tab, File),
 3288:     ok = open_w(Tab, File, 0, Config),
 3289:     ok = open_w(Tab, File, 100, Config),
 3290:     ok.
 3291: 
 3292: %% One process logs and another process closes the log. Before
 3293: %% monitors were used, this would make the client never return.
 3294: monit(Tab, File) ->
 3295:     file:delete(File),
 3296:     {ok, Tab} = dets:open_file(Tab, [{file,File}]),
 3297:     F1 = fun() -> dets:close(Tab) end,
 3298:     F2 = fun() -> {'EXIT', {badarg, _}} = do_log(Tab) end,
 3299:     spawn(F2),
 3300:     timer:sleep(100),
 3301:     spawn(F1),
 3302:     dets:close(Tab),
 3303:     ok = file:delete(File).
 3304: 
 3305: do_log(Tab) ->
 3306:     case catch dets:insert(Tab, {hej,san,sa}) of
 3307:         ok -> do_log(Tab);
 3308:         Else -> Else
 3309:     end.
 3310: 
 3311: %% Kill the Dets process while repair is in progress.
 3312: kill_while_repairing(Tab, File) ->
 3313:     create_opened_log(File),
 3314:     Delay = 1000,
 3315:     dets:start(),
 3316:     Parent = self(),
 3317:     Ps = processes(),
 3318:     F = fun() -> 
 3319:                 R = (catch dets:open_file(Tab, [{file,File}])),
 3320:                 timer:sleep(Delay),
 3321:                 Parent ! {self(), R}
 3322:         end,
 3323:     %% One of these will open the file, the other will be pending
 3324:     %% until the file has been repaired:
 3325:     P1 = spawn(F),
 3326:     P2 = spawn(F),
 3327:     P3 = spawn(F),
 3328:     DetsPid = find_dets_pid([P1, P2, P3 | Ps]),
 3329:     exit(DetsPid, kill),
 3330: 
 3331:     receive {P1,R1} -> R1 end,
 3332:     receive {P2,R2} -> R2 end,
 3333:     receive {P3,R3} -> R3 end,
 3334:     io:format("Killed pid: ~p~n", [DetsPid]),
 3335:     io:format("Remaining Dets-pids (should be nil): ~p~n",
 3336:               [find_dets_pids()]),
 3337:     {replies,[{'EXIT', {dets_process_died, _}}, {ok,_}, {ok, _}]} =
 3338:          {replies,lists:sort([R1, R2, R3])},
 3339: 
 3340:     timer:sleep(200),
 3341:     case dets:info(Tab) of
 3342:         undefined -> 
 3343:             ok;
 3344:         _Info ->
 3345:             timer:sleep(5000),
 3346:             undefined = dets:info(Tab)
 3347:     end,
 3348: 
 3349:     file:delete(File),
 3350:     ok.
 3351: 
 3352: find_dets_pid(P0) ->
 3353:     case lists:sort(processes() -- P0) of
 3354:         [P, _] -> P;
 3355:         _ -> timer:sleep(100), find_dets_pid(P0)
 3356:     end.
 3357: 
 3358: find_dets_pid() ->
 3359:     case find_dets_pids() of
 3360:         [] ->
 3361:             timer:sleep(100),
 3362:             find_dets_pid();
 3363:         [Pid] ->
 3364:             Pid
 3365:     end.
 3366: 
 3367: find_dets_pids() ->
 3368:     lists:filter(fun(P) -> dets:pid2name(P) =/= undefined end,
 3369:                  erlang:processes()).
 3370: 
 3371: %% Kill the Dets process when there are users and an on-going
 3372: %% initiailization.
 3373: kill_while_init(Tab, File) ->
 3374:     file:delete(File),
 3375:     Parent = self(),
 3376:     F = fun() -> 
 3377:                 R = dets:open_file(Tab, [{file,File}]),
 3378:                 Parent ! {self(), R},
 3379:                 receive {Parent, die} -> ok end,
 3380:                 {error, not_owner} = dets:close(Tab)
 3381:         end,
 3382:     P1 = spawn(F),
 3383:     P2 = spawn(F),
 3384:     P3 = spawn(F),
 3385:     IF = fun() ->
 3386:                  R = dets:open_file(Tab, [{file,File}]),
 3387:                  Parent ! {self(), R},
 3388:                  Fun = fun(_) -> timer:sleep(100000) end,
 3389:                  {'EXIT', {badarg, _}} = (catch dets:init_table(Tab, Fun)),
 3390:                  receive {Parent, die} -> ok end
 3391:           end,
 3392:     P4 = spawn(IF),
 3393:     receive {P1,R1} -> {ok, _} = R1 end,
 3394:     receive {P2,R2} -> {ok, _} = R2 end,
 3395:     receive {P3,R3} -> {ok, _} = R3 end,
 3396:     receive {P4,R4} -> {ok, _} = R4 end,
 3397:     DetsPid = find_dets_pid(),
 3398:     exit(DetsPid, kill),
 3399:     
 3400:     timer:sleep(1000),
 3401:     undefined = dets:info(Tab),
 3402:     P1 ! {Parent, die},
 3403:     P2 ! {Parent, die},
 3404:     P3 ! {Parent, die},
 3405:     P4 ! {Parent, die},
 3406: 
 3407:     file:delete(File),
 3408:     timer:sleep(100),
 3409:     ok.
 3410: 
 3411: open_ro(Tab, File) ->
 3412:     create_opened_log(File),
 3413:     Delay = 1000,
 3414:     Parent = self(),
 3415:     F = fun() ->
 3416:                 R = dets:open_file(Tab, [{file,File},{access,read}]),
 3417:                 timer:sleep(Delay),
 3418:                 Parent ! {self(), R}
 3419:         end,
 3420:     P1 = spawn(F),
 3421:     P2 = spawn(F),
 3422:     P3 = spawn(F),
 3423:     
 3424:     receive {P1,R1} -> {error,{not_closed,_}} = R1 end,
 3425:     receive {P2,R2} -> {error,{not_closed,_}} = R2 end,
 3426:     receive {P3,R3} -> {error,{not_closed,_}} = R3 end,
 3427:     ok.
 3428: 
 3429: open_w(Tab, File, Delay, Config) ->
 3430:     create_opened_log(File),
 3431:     Parent = self(),
 3432:     F = fun() -> 
 3433:                 R = dets:open_file(Tab, [{file,File}]),
 3434:                 timer:sleep(Delay),
 3435:                 Parent ! {self(), R}
 3436:         end,
 3437:     Pid1 = spawn(F),
 3438:     Pid2 = spawn(F),
 3439:     Pid3 = spawn(F),
 3440:     undefined = dets:info(Tab), % is repairing now
 3441:     0 = qlen(),
 3442: 
 3443:     Tab2 = t2,
 3444:     File2 = filename(Tab2, Config),
 3445:     file:delete(File2),
 3446:     {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]),
 3447:     ok = dets:close(Tab2),
 3448:     file:delete(File2),
 3449:     0 = qlen(), % still repairing
 3450: 
 3451:     receive {Pid1,R1} -> {ok, Tab} = R1 end,
 3452:     receive {Pid2,R2} -> {ok, Tab} = R2 end,
 3453:     receive {Pid3,R3} -> {ok, Tab} = R3 end,
 3454:     timer:sleep(200),
 3455:     case dets:info(Tab) of
 3456:         undefined -> 
 3457:             ok;
 3458:         _Info ->
 3459:             timer:sleep(5000),
 3460:             undefined = dets:info(Tab)
 3461:     end,
 3462: 
 3463:     file:delete(File),
 3464:     ok.
 3465: 
 3466: qlen() ->
 3467:     {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())),
 3468:     N.
 3469: 
 3470: create_opened_log(File) ->
 3471:     Tab = t,
 3472:     file:delete(File),
 3473:     {ok, Tab} = dets:open_file(Tab, [{file,File}]),
 3474:     ok = ins(Tab, 60000),
 3475:     ok = dets:close(Tab),
 3476:     crash(File, ?CLOSED_PROPERLY_POS+3, ?NOT_PROPERLY_CLOSED),
 3477:     ok.
 3478: 
 3479: insert_new(doc) ->
 3480:     ["OTP-5075. insert_new/2"];
 3481: insert_new(suite) ->
 3482:     [];
 3483: insert_new(Config) ->
 3484:     Tab = insert_new,
 3485:     File = filename(Tab, Config),
 3486:     file:delete(File),
 3487:     {ok, T} = dets:open_file(Tab, [{file,File}]),
 3488:     {'EXIT', {badarg, _}} = (catch dets:insert_new(Tab, 14)),
 3489:     {'EXIT', {badarg, _}} = (catch dets:insert_new(Tab, {})),
 3490:     true = dets:insert_new(Tab, {1,a}),
 3491:     false = dets:insert_new(Tab, {1,a}),
 3492:     true = dets:insert_new(Tab, [{2,b}, {3,c}]),
 3493:     false = dets:insert_new(Tab, [{2,b}, {3,c}]),
 3494:     false = dets:insert_new(Tab, [{1,a}, {4,d}]),
 3495:     ok = dets:close(Tab),
 3496: 
 3497:     file:delete(File),    
 3498:     {ok, T} = dets:open_file(Tab, [{file,File},{type,bag}]),
 3499:     true = dets:insert_new(Tab, {1,a}),
 3500:     false = dets:insert_new(Tab, {1,b}),
 3501:     true = dets:insert_new(Tab, [{2,b}, {3,c}]),
 3502:     false = dets:insert_new(Tab, [{2,a}, {3,d}]),
 3503:     false = dets:insert_new(Tab, [{1,a}, {4,d}]),
 3504:     ok = dets:close(Tab),
 3505:     
 3506: 
 3507:     file:delete(File),
 3508:     ok.
 3509:     
 3510: repair_continuation(doc) ->
 3511:     ["OTP-5126. repair_continuation/2"];
 3512: repair_continuation(suite) ->
 3513:     [];
 3514: repair_continuation(Config) ->
 3515:     Tab = repair_continuation_table,
 3516:     Fname = filename(repair_cont, Config),
 3517:     file:delete(Fname),
 3518:     {ok, _} = dets:open_file(Tab, [{file,Fname}]),
 3519:     ok = dets:insert(Tab, [{1,a},{2,b},{3,c}]),
 3520: 
 3521:     MS = [{'_',[],[true]}],
 3522: 
 3523:     {[true], C1} = dets:select(Tab, MS, 1),
 3524:     C2 = binary_to_term(term_to_binary(C1)),
 3525:     {'EXIT', {badarg, _}} = (catch dets:select(C2)),
 3526:     C3 = dets:repair_continuation(C2, MS),
 3527:     {[true], C4} = dets:select(C3),
 3528:     C5 = dets:repair_continuation(C4, MS),
 3529:     {[true], _} = dets:select(C5),
 3530:     {'EXIT', {badarg, _}} = (catch dets:repair_continuation(Tab, bu)),
 3531: 
 3532:     ok = dets:close(Tab),
 3533:     file:delete(Fname),
 3534:     ok.
 3535: 
 3536: otp_5487(doc) ->
 3537:     ["OTP-5487. Growth of read-only table (again)."];
 3538: otp_5487(suite) ->
 3539:     [];
 3540: otp_5487(Config) ->
 3541:     otp_5487(Config, 9),
 3542:     otp_5487(Config, 8),
 3543:     ok.
 3544: 
 3545: otp_5487(Config, Version) ->
 3546:     Tab = otp_5487,
 3547:     Fname = filename(otp_5487, Config),
 3548:     file:delete(Fname),
 3549:     Ets = ets:new(otp_5487, [public, set]),
 3550:     lists:foreach(fun(I) -> ets:insert(Ets, {I,I+1}) end,
 3551:                   lists:seq(0,1000)),
 3552:     {ok, _} = dets:open_file(Tab, [{file,Fname},{version,Version}]),
 3553:     ok = dets:from_ets(Tab, Ets),
 3554:     ok = dets:sync(Tab),
 3555:     ok = dets:close(Tab),
 3556:     {ok, _} = dets:open_file(Tab, [{file,Fname},{access,read}]),
 3557:     [{1,2}] = dets:lookup(Tab, 1),
 3558:     ok = dets:close(Tab),
 3559:     ets:delete(Ets),
 3560:     file:delete(Fname).
 3561: 
 3562: otp_6206(doc) ->
 3563:     ["OTP-6206. Badly formed free lists."];
 3564: otp_6206(suite) ->
 3565:     [];
 3566: otp_6206(Config) ->
 3567:     Tab = otp_6206,
 3568:     File = filename(Tab, Config),
 3569: 
 3570:     file:delete(File),
 3571:     Options = [{file,File}],
 3572:     {ok, Tab} = dets:open_file(Tab, Options),
 3573:     NObjs = 13006,
 3574:     ok = ins(Tab, NObjs),
 3575:     ok = del(Tab, NObjs, 2),
 3576:     ok = dets:close(Tab),
 3577: 
 3578:     %% Used to return {badmatch,{error,{bad_freelists,File}}.
 3579:     {ok, Tab} = dets:open_file(Tab, [{repair,false}|Options]),
 3580:     ok = dets:close(Tab),
 3581:     file:delete(File),
 3582:     ok.
 3583: 
 3584: otp_6359(doc) ->
 3585:     ["OTP-6359. select and match never return the empty list."];
 3586: otp_6359(suite) ->
 3587:     [];
 3588: otp_6359(Config) ->
 3589:     Tab = otp_6359,
 3590:     File = filename(Tab, Config),
 3591: 
 3592:     file:delete(File),
 3593:     {ok, _} = dets:open_file(Tab, [{file, File}]),
 3594:     %% Used to return {[], Cont}:
 3595:     '$end_of_table' = dets:match(Tab, '_', 100),
 3596:     ok = dets:close(Tab),
 3597:     file:delete(File),
 3598:     ok.
 3599: 
 3600: otp_4738(doc) ->
 3601:     ["OTP-4738. ==/2 and =:=/2."];
 3602: otp_4738(suite) ->
 3603:     [];
 3604: otp_4738(Config) ->
 3605:     %% Version 8 has not been corrected.
 3606:     %% (The constant -12857447 is for version 9 only.)
 3607:     otp_4738_set(9, Config),
 3608:     otp_4738_bag(9, Config),
 3609:     otp_4738_dupbag(9, Config),
 3610:     ok.
 3611: 
 3612: otp_4738_dupbag(Version, Config) ->
 3613:     Tab = otp_4738,
 3614:     File = filename(Tab, Config),
 3615:     file:delete(File),
 3616:     I = -12857447,
 3617:     F = float(I),
 3618:     One = 1,
 3619:     FOne = float(One),
 3620:     Args = [{file,File},{type,duplicate_bag},{version,Version}],
 3621:     {ok, Tab} = dets:open_file(Tab, Args),
 3622:     ok = dets:insert(Tab, [{I,One},{F,One},{I,FOne},{F,FOne}]),
 3623:     ok = dets:sync(Tab),
 3624:     [{F,One},{F,FOne}] = dets:lookup(Tab, F),
 3625:     [{I,One},{I,FOne}] = dets:lookup(Tab, I),
 3626:     ok = dets:insert(Tab, [{F,One},{F,FOne}]),
 3627:     [{I,One},{I,FOne},{F,One},{F,FOne},{F,One},{F,FOne}] =
 3628:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3629:     ok = dets:insert(Tab, [{F,FOne},{F,One}]),
 3630:     [{I,One},{I,FOne},{F,One},{F,FOne},{F,One},
 3631:            {F,FOne},{F,FOne},{F,One}] = 
 3632:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3633:     ok = dets:delete_object(Tab, {I,FOne}),
 3634:     [{I,One},{F,One},{F,FOne},{F,One},{F,FOne},{F,FOne},{F,One}] =
 3635:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3636:     ok = dets:insert(Tab, {I,FOne}),
 3637:     [{I,One},{I,FOne},{F,One},{F,FOne},{F,One},
 3638:            {F,FOne},{F,FOne},{F,One}] = 
 3639:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3640:     ok = dets:delete_object(Tab, {F,FOne}),
 3641:     [{I,One},{I,FOne},{F,One},{F,One},{F,One}] =
 3642:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3643:     ok = dets:delete(Tab, F),
 3644:     [{I,One},{I,FOne}] = dets:match_object(Tab, '_'),
 3645:     ok = dets:close(Tab),
 3646:     file:delete(File),
 3647: 
 3648:     Zero = 0,
 3649:     FZero = float(Zero),
 3650:     {ok, Tab} = dets:open_file(Tab, Args),
 3651:     ok = dets:insert(Tab, [{I,One},{F,One},{I,FOne},{F,FOne}]),
 3652:     ok = dets:insert(Tab, [{I,One},{F,One},{I,FOne},{F,FOne}]),
 3653:     ok = dets:insert(Tab, [{I,Zero},{F,Zero},{I,FZero},{I,FZero}]),
 3654:     Objs0 = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3655:     ok = dets:close(Tab),
 3656:     crash(File, ?CLOSED_PROPERLY_POS+3, ?NOT_PROPERLY_CLOSED),
 3657:     io:format("Expect repair:~n"),
 3658:     {ok, Tab} = dets:open_file(Tab, Args),
 3659:     Objs1 = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3660:     ok = dets:close(Tab),
 3661:     Objs1 = Objs0,
 3662:     file:delete(File),
 3663:     ok.
 3664: 
 3665: otp_4738_bag(Version, Config) ->
 3666:     Tab = otp_4738,
 3667:     File = filename(Tab, Config),
 3668:     file:delete(File),
 3669:     I = -12857447,
 3670:     F = float(I),
 3671:     One = 1,
 3672:     FOne = float(One),
 3673:     Args = [{file,File},{type,bag},{version,Version}],
 3674:     {ok, Tab} = dets:open_file(Tab, Args),
 3675:     ok = dets:insert(Tab, [{I,One},{F,One},{I,FOne},{F,FOne}]),
 3676:     ok = dets:sync(Tab),
 3677:     [{F,One},{F,FOne}] = dets:lookup(Tab, F),
 3678:     [{I,One},{I,FOne}] = dets:lookup(Tab, I),
 3679:     ok = dets:insert(Tab, [{F,One},{F,FOne}]),
 3680:     [{I,One},{I,FOne},{F,One},{F,FOne}] =
 3681:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3682:     ok = dets:insert(Tab, [{F,FOne},{F,One}]),
 3683:     [{I,One},{I,FOne},{F,FOne},{F,One}] =
 3684:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3685:     ok = dets:delete_object(Tab, {I,FOne}),
 3686:     [{I,One},{F,FOne},{F,One}] =
 3687:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3688:     ok = dets:insert(Tab, {I,FOne}),
 3689:     [{I,One},{I,FOne},{F,FOne},{F,One}] =
 3690:         dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3691:     ok = dets:delete(Tab, F),
 3692:     [{I,One},{I,FOne}] = dets:match_object(Tab, '_'),
 3693:     ok = dets:close(Tab),
 3694:     file:delete(File).
 3695: 
 3696: otp_4738_set(Version, Config) ->
 3697:     Tab = otp_4738,
 3698:     File = filename(Tab, Config),
 3699:     file:delete(File),
 3700:     Args = [{file,File},{type,set},{version,Version}],
 3701: 
 3702:     %% I and F share the same slot.
 3703:     I = -12857447,
 3704:     F = float(I),
 3705:     {ok, Tab} = dets:open_file(Tab, Args),
 3706:     ok = dets:insert(Tab, [{I},{F}]),
 3707:     ok = dets:sync(Tab),
 3708:     [{F}] = dets:lookup(Tab, F),
 3709:     [{I}] = dets:lookup(Tab, I),
 3710:     ok = dets:insert(Tab, [{F}]),
 3711:     [{I},{F}] = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3712:     ok = dets:close(Tab),
 3713:     file:delete(File),
 3714:     
 3715:     {ok, Tab} = dets:open_file(Tab, Args),
 3716:     ok = dets:insert(Tab, [{I}]),
 3717:     ok = dets:sync(Tab),
 3718:     [] = dets:lookup(Tab, F),
 3719:     [{I}] = dets:lookup(Tab, I),
 3720:     ok = dets:insert(Tab, [{F}]),
 3721:     [{I},{F}] = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3722:     ok = dets:close(Tab),
 3723:     file:delete(File),
 3724: 
 3725:     {ok, Tab} = dets:open_file(Tab, Args),
 3726:     ok = dets:insert(Tab, [{I},{F}]),
 3727:     %% {insert, ...} in the cache, try lookup:
 3728:     [{F}] = dets:lookup(Tab, F),
 3729:     [{I}] = dets:lookup(Tab, I),
 3730:     %% Both were found, but that cannot be verified.
 3731:     [{I},{F}] = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3732:     ok = dets:close(Tab),
 3733:     file:delete(File),
 3734: 
 3735:     {ok, Tab} = dets:open_file(Tab, Args),
 3736:     ok = dets:insert(Tab, [{I}]),
 3737:     ok = dets:sync(Tab),
 3738:     ok = dets:insert(Tab, [{F}]),
 3739:     %% {insert, ...} in the cache, try lookup:
 3740:     [{F}] = dets:lookup(Tab, F),
 3741:     [{I}] = dets:lookup(Tab, I),
 3742:     [{I},{F}] = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3743:     ok = dets:close(Tab),
 3744:     file:delete(File),
 3745: 
 3746:     {ok, Tab} = dets:open_file(Tab, Args),
 3747:     %% Both operations in the cache:
 3748:     ok = dets:insert(Tab, [{I}]),
 3749:     ok = dets:insert(Tab, [{F}]),
 3750:     [{I},{F}] = dets_utils:mkeysort(1, dets:match_object(Tab, '_')),
 3751:     ok = dets:close(Tab),
 3752:     file:delete(File),
 3753:     ok.
 3754: 
 3755: otp_7146(doc) ->
 3756:     ["OTP-7146. Bugfix: missing test when re-hashing."];
 3757: otp_7146(suite) ->
 3758:     [];
 3759: otp_7146(Config) ->
 3760:     Tab = otp_7146,
 3761:     File = filename(Tab, Config),
 3762:     file:delete(File),
 3763: 
 3764:     Max = 2048,
 3765:     {ok, Tab} = dets:open_file(Tab, [{max_no_slots,Max}, {file,File}]),
 3766:     write_dets(Tab, Max),
 3767:     ok = dets:close(Tab),
 3768: 
 3769:     file:delete(File),
 3770:     ok.
 3771: 
 3772: write_dets(Tab, Max) ->
 3773:     write_dets(Tab, 0, Max).
 3774: 
 3775: write_dets(_Tab, N, Max) when N > Max ->
 3776:     ok;
 3777: write_dets(Tab, N, Max) ->
 3778:     ok = dets:insert(Tab,{ N, {entry,N}}),
 3779:     write_dets(Tab, N+1, Max).
 3780: 
 3781: otp_8070(doc) ->
 3782:     ["OTP-8070. Duplicated objects with insert_new() and duplicate_bag."];
 3783: otp_8070(suite) ->
 3784:     [];
 3785: otp_8070(Config) when is_list(Config) ->
 3786:     Tab = otp_8070,
 3787:     File = filename(Tab, Config),
 3788:     file:delete(File),
 3789:     {ok, _} = dets:open_file(Tab, [{file,File},{type, duplicate_bag}]),
 3790:     ok = dets:insert(Tab, [{3,0}]),
 3791:     false = dets:insert_new(Tab, [{3,1},{3,1}]),
 3792:     [{3,0}] = dets:lookup(Tab, 3),
 3793:     ok = dets:close(Tab),
 3794:     file:delete(File),
 3795:     ok.
 3796: 
 3797: otp_8856(doc) ->
 3798:     ["OTP-8856. insert_new() bug."];
 3799: otp_8856(suite) ->
 3800:     [];
 3801: otp_8856(Config) when is_list(Config) ->
 3802:     Tab = otp_8856,
 3803:     File = filename(Tab, Config),
 3804:     file:delete(File),
 3805:     Me = self(),
 3806:     {ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]),
 3807:     spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end),
 3808:     spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end),
 3809:     ok = dets:close(Tab),
 3810:     receive {1, ok} -> ok end,
 3811:     receive {2, true} -> ok end,
 3812:     file:delete(File),
 3813: 
 3814:     {ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]),
 3815:     spawn(fun() -> dets:delete(Tab, 0) end),
 3816:     spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end),
 3817:     ok = dets:close(Tab),
 3818:     receive {3, true} -> ok end,
 3819:     file:delete(File),
 3820:     ok.
 3821: 
 3822: otp_8903(doc) ->
 3823:     ["OTP-8903. bchunk/match/select bug."];
 3824: otp_8903(suite) ->
 3825:     [];
 3826: otp_8903(Config) when is_list(Config) ->
 3827:     Tab = otp_8903,
 3828:     File = filename(Tab, Config),
 3829:     {ok,T} = dets:open_file(bug, [{file,File}]),
 3830:     ok = dets:insert(T, [{1,a},{2,b},{3,c}]),
 3831:     dets:safe_fixtable(T, true),
 3832:     {[_],C1} = dets:match_object(T, '_', 1),
 3833:     {BC1,_D} = dets:bchunk(T, start),
 3834:     ok = dets:close(T),
 3835:     {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
 3836:     {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
 3837:     {ok,T} = dets:open_file(bug, [{file,File}]),
 3838:     false = dets:info(T, safe_fixed),
 3839:     {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
 3840:     {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
 3841:     ok = dets:close(T),
 3842:     file:delete(File),
 3843:     ok.
 3844: 
 3845: otp_8923(doc) ->
 3846:     ["OTP-8923. rehash due to lookup after initialization."];
 3847: otp_8923(suite) ->
 3848:     [];
 3849: otp_8923(Config) when is_list(Config) ->
 3850:     Tab = otp_8923,
 3851:     File = filename(Tab, Config),
 3852:     %% Create a file with more than 256 keys:
 3853:     file:delete(File),
 3854:     Bin = list_to_binary([ 0 || _ <- lists:seq(1, 400) ]),
 3855:     BigBin = list_to_binary([ 0 ||_ <- lists:seq(1, 4000)]),
 3856:     Ets = ets:new(temp, [{keypos,1}]),
 3857:     [ true = ets:insert(Ets, {C,Bin}) || C <- lists:seq(1, 700) ],
 3858:     true = ets:insert(Ets, {helper_data,BigBin}),
 3859:     true = ets:insert(Ets, {prim_btree,BigBin}),
 3860:     true = ets:insert(Ets, {sec_btree,BigBin}),
 3861:     %% Note: too few slots; re-hash will take place
 3862:     {ok, Tab} = dets:open_file(Tab, [{file,File}]),
 3863:     Tab = ets:to_dets(Ets, Tab),
 3864:     ok = dets:close(Tab),
 3865:     true = ets:delete(Ets),
 3866:     
 3867:     {ok,Ref} = dets:open_file(File),
 3868:     [{1,_}] = dets:lookup(Ref, 1),
 3869:     ok = dets:close(Ref),
 3870: 
 3871:     {ok,Ref2} = dets:open_file(File),
 3872:     [{helper_data,_}] = dets:lookup(Ref2, helper_data),
 3873:     ok = dets:close(Ref2),
 3874: 
 3875:     file:delete(File),
 3876:     ok.
 3877: 
 3878: otp_9282(doc) ->
 3879:     ["OTP-9282. The name of a table can be an arbitrary term"];
 3880: otp_9282(suite) ->
 3881:     [];
 3882: otp_9282(Config) when is_list(Config) ->
 3883:     some_calls(make_ref(), Config),
 3884:     some_calls({a,typical,name}, Config),
 3885:     some_calls(fun() -> a_funny_name end, Config),
 3886:     ok.
 3887: 
 3888: some_calls(Tab, Config) ->
 3889:     File = filename(ref, Config),
 3890:     {ok,T} = dets:open_file(Tab, [{file,File}]),
 3891:     T = Tab,
 3892:     false = dets:info(T, safe_fixed),
 3893:     File = dets:info(T, filename),
 3894:     ok = dets:insert(Tab, [{3,0}]),
 3895:     [{3,0}] = dets:lookup(Tab, 3),
 3896:     [{3,0}] = dets:traverse(Tab, fun(X) -> {continue, X} end),
 3897:     ok = dets:close(T),
 3898:     file:delete(File).
 3899: 
 3900: 
 3901: otp_11245(doc) ->
 3902:     ["OTP-11245. Tables remained fixed after traversal"];
 3903: otp_11245(suite) ->
 3904:     [];
 3905: otp_11245(Config) when is_list(Config) ->
 3906:     Tab = otp_11245,
 3907:     File = filename(Tab, Config),
 3908:     {ok, Tab} = dets:open_file(Tab, [{file,File}]),
 3909:     N = 1024,
 3910:     ins(Tab, N),
 3911:     N = length(dets:match(Tab, '_')),
 3912:     false = dets:info(Tab, safe_fixed),
 3913:     dets:traverse(Tab, fun(_) -> continue end),
 3914:     false = dets:info(Tab, safe_fixed),
 3915:     N = dets:foldl(fun(_, N2) -> N2+1 end, 0, Tab),
 3916:     false = dets:info(Tab, safe_fixed),
 3917:     N = dets:foldr(fun(_, N2) -> N2+1 end, 0, Tab),
 3918:     false = dets:info(Tab, safe_fixed),
 3919:     ok = dets:close(Tab),
 3920:     file:delete(File),
 3921:     ok.
 3922: 
 3923: %%
 3924: %% Parts common to several test cases
 3925: %% 
 3926: 
 3927: start_node_rel(Name, Rel, How) ->
 3928:     Release = [{release, atom_to_list(Rel)}],
 3929:     Pa = filename:dirname(code:which(?MODULE)),
 3930:     test_server:start_node(Name, How,
 3931:                            [{args,
 3932:                              " -kernel net_setuptime 100 "
 3933:                              " -pa " ++ Pa},
 3934:                             {erl, Release}]).
 3935: 
 3936: crash(File, Where) ->
 3937:     crash(File, Where, 10).
 3938: 
 3939: crash(File, Where, What) when is_integer(What) ->
 3940:     {ok, Fd} = file:open(File, [read,write]),
 3941:     file:position(Fd, Where),
 3942:     ok = file:write(Fd, [What]),
 3943:     ok = file:close(Fd).
 3944: 
 3945: args(Config) ->
 3946:     {Sets, Bags, Dups} = 
 3947: 	{[
 3948: 	  [], 
 3949: 	  [{type, set}, {estimated_no_objects, 300},  
 3950: 	   {ram_file, true}],
 3951: 	  [{type, set}, {estimated_no_objects, 300}],
 3952: 	  [{type, set}, {estimated_no_objects, 300}],
 3953: 	  [{auto_save,20}, {type, set}, 
 3954: 	   {estimated_no_objects, 300}] 
 3955: 	 ],
 3956: 
 3957: 	 [
 3958: 	  [{type, bag}, {estimated_no_objects, 300}, {ram_file, true}],
 3959: 	  [{type, bag}],
 3960: 	  [{type, bag}, {estimated_no_objects, 300}],
 3961: 	  [{type, bag}, {estimated_no_objects, 300}],
 3962: 	  [{type, bag}, 
 3963: 	   {auto_save,20}, {estimated_no_objects, 300}],
 3964: 	  [{type, bag}, {estimated_no_objects, 300},   {ram_file, true}]
 3965: 	 ],
 3966: 
 3967: 	 [
 3968: 	  [{type, duplicate_bag}, {estimated_no_objects, 300}, 
 3969: 	   {ram_file, true}],
 3970: 	  [{type, duplicate_bag}],
 3971: 	  [{type, duplicate_bag}, {estimated_no_objects, 300}],
 3972: 	  [{type, duplicate_bag}, {estimated_no_objects, 300}],
 3973: 	  [{type, duplicate_bag},
 3974: 	   {auto_save,20}, {estimated_no_objects, 300}],
 3975: 	  [{type, duplicate_bag}, {estimated_no_objects, 300},   
 3976: 	   {ram_file, true}]
 3977: 	 ]
 3978: 	},
 3979:     zip_filename(Sets, Bags, Dups, Config).
 3980: 
 3981: zip_filename(S, B, D, Conf) ->
 3982:     zip_filename(S, B, D, [], [], [], 1, Conf).
 3983: 
 3984: zip_filename([H|T], B, D, S1, B1, D1, I, Conf) ->
 3985:     zip_filename(T, B, D, [[{file, new_filename(I, Conf)} | H] | S1],
 3986: 		 B1, D1, I+1, Conf);
 3987: zip_filename([], [H|B], D, S1, B1, D1, I, Conf) ->
 3988:     zip_filename([], B, D, S1, [[{file, new_filename(I, Conf)} | H] | B1],
 3989: 		 D1, I+1, Conf);
 3990: zip_filename([], [], [H|T], S1, B1, D1, I, Conf) ->
 3991:     zip_filename([], [], T, S1, B1, [[{file, new_filename(I, Conf)} | H] | D1],
 3992: 		 I+1, Conf);
 3993: zip_filename([], [], [], S1, B1, D1, _, _Conf) ->
 3994:     {reverse(S1), reverse(B1), reverse(D1)}.
 3995: 
 3996: del_test(Tab) ->
 3997:     ?format("Deltest on ~p~n", [Tab]),
 3998:     Objs = safe_get_all_objects(Tab),
 3999:     Keys = map(fun(X) -> element(1, X) end, Objs),
 4000:     foreach(fun(Key) -> dets:delete(Tab, Key) end, Keys),
 4001:     0 = length(get_all_objects(Tab)),
 4002:     [] = get_all_objects_fast(Tab),
 4003:     0 = dets:info(Tab, size).
 4004: 
 4005: del_obj_test(Tab) ->
 4006:     ?format("Delobjtest on ~p~n", [Tab]),
 4007:     Objs = safe_get_all_objects(Tab),
 4008:     LL = length(Objs),
 4009:     LL = dets:info(Tab, size),
 4010:     foreach(fun(Obj) -> dets:delete_object(Tab, Obj) end, Objs),
 4011:     0 = length(get_all_objects(Tab)),
 4012:     [] = get_all_objects_fast(Tab),
 4013:     0 = dets:info(Tab, size).
 4014: 
 4015: match_del_test(Tab) ->
 4016:     ?format("Match delete test on ~p~n", [Tab]),
 4017:     ok = dets:match_delete(Tab, {'_','_','_'}),
 4018:     Sz = dets:info(Tab, size),
 4019:     true = Sz =:= length(dets:match_object(Tab, '_')),
 4020:     ok = dets:match_delete(Tab, '_'),
 4021:     0 = dets:info(Tab, size),
 4022:     0 = length(get_all_objects(Tab)),
 4023:     [] = get_all_objects_fast(Tab).
 4024: 
 4025: trav_test(_Data, Len, Tab) ->
 4026:     ?format("Travtest on ~p~n", [Tab]),
 4027:     _X0 = dets:traverse(Tab, fun(_X) -> continue end),
 4028:     XX = dets:traverse(Tab, fun(X) -> {continue, X} end),
 4029:     case Len =:= length(XX) of
 4030: 	      false -> ?format("DIFF ~p~n", [XX -- _Data]);
 4031: 	      true -> ok
 4032: 	  end,
 4033:     1 = length(dets:traverse(Tab, fun(X) -> {done, X} end)).
 4034: 
 4035: match_test(Data, Tab) ->
 4036:     ?format("Match test on ~p~n", [Tab]),
 4037:     Data1 = sort(filter(fun(X) when tuple_size(X) =:= 3 -> true;
 4038:                            (_X) -> false
 4039:                         end, Data)),
 4040:     Data1 = sort(dets:match_object(Tab, {'$1', '$2', '$3'})),
 4041: 
 4042:     Len = length(Data),
 4043:     Len = length(dets:match(Tab, '_')),
 4044:     Len2 = length(Data1),
 4045:     Len2 = length(dets:match(Tab, {'$1', '_', '_'})),
 4046:     
 4047:     Data3 =
 4048: 	filter(fun(X) ->
 4049: 		       K = element(1, X),
 4050: 		       if
 4051: 			   tuple_size(X) =:= 3, tuple_size(K) =:= 2 -> true;
 4052: 			   true -> false
 4053: 		       end
 4054: 	       end, Data),
 4055:     Len3 = length(Data3),
 4056:     Len3 = length(dets:match(Tab, {{'$1', '$2'}, '_', '_'})),
 4057:     Len3 = length(dets:match_object(Tab, {{'$1', '$2'}, '_', '_'})),
 4058:     
 4059:     R = make_ref(),
 4060:     dets:insert(Tab, {{R, R}, 33 ,44}),
 4061:     1 = length(dets:match(Tab, {{R, R}, '_', '_'})),
 4062:     1 = length(dets:match_object(Tab, {{R, R}, '_', '_'})).
 4063: 
 4064: %%
 4065: %% Utilities
 4066: %%
 4067: 
 4068: headsz(8) ->
 4069:     ?HEADSZ_v8;
 4070: headsz(_) ->
 4071:     ?HEADSZ_v9.
 4072: 
 4073: unwritable(Fname) ->
 4074:     {ok, Info} = file:read_file_info(Fname),
 4075:     Mode = Info#file_info.mode - 8#00200,
 4076:     file:write_file_info(Fname, Info#file_info{mode = Mode}).
 4077: 
 4078: writable(Fname) ->
 4079:     {ok, Info} = file:read_file_info(Fname),
 4080:     Mode = Info#file_info.mode bor 8#00200,
 4081:     file:write_file_info(Fname, Info#file_info{mode = Mode}).
 4082: 
 4083: truncate(File, Where) ->
 4084:     {ok, Fd} = file:open(File, [read,write]),
 4085:     file:position(Fd, Where),
 4086:     ok = file:truncate(Fd),
 4087:     ok = file:close(Fd).
 4088: 
 4089: new_filename(Name, _Config) when is_integer(Name) ->
 4090:     filename:join(?privdir(_Config),
 4091: 		  integer_to_list(Name) ++ ".DETS").
 4092: 
 4093: filename(Name, Config) when is_atom(Name) ->
 4094:     filename(atom_to_list(Name), Config);
 4095: filename(Name, _Config) ->
 4096:     filename:join(?privdir(_Config), Name).
 4097: 
 4098: open_files(_Name, [], _Version) ->
 4099:     [];
 4100: open_files(Name0, [Args | Tail], Version) ->
 4101:     ?format("init ~p~n", [Args]),
 4102:     Name = list_to_atom(integer_to_list(Name0)),
 4103:     {ok, Name} = dets:open_file(Name, [{version,Version} | Args]),
 4104:     [Name | open_files(Name0+1, Tail, Version)].
 4105:     
 4106: close_all(Tabs) -> foreach(fun(Tab) -> ok = dets:close(Tab) end, Tabs).
 4107: 
 4108: delete_files(Args) ->   
 4109:     Fun = fun(F) ->
 4110: 		  {value, {file, File}} = keysearch(file, 1, F),
 4111: 		  file:delete(File),
 4112: 		  File
 4113: 	  end,
 4114:     map(Fun, Args).
 4115: 
 4116: %% Initialize all tables
 4117: initialize(Tabs, Data) ->
 4118:     foreach(fun(Tab) ->
 4119:                     Fun = fun(Obj) -> ok =  dets:insert(Tab, Obj) end,
 4120:                     foreach(Fun, Data),
 4121:                     dets:sync(Tab)
 4122:             end, Tabs).
 4123: 
 4124: %% need more than 512 objects to really trig overflow
 4125: make_data(Kp) ->
 4126:     make_data(Kp, set).
 4127: 
 4128: make_data(Kp, Type) ->
 4129:     dup(Type, make_data(Kp, Type, 520)).
 4130: 
 4131: dup(duplicate_bag, [H1, H2 |T]) ->
 4132:     [H1,H2, H1, H2 | dup(duplicate_bag, T)];
 4133: dup(_, Other) ->
 4134:     Other.
 4135:     
 4136: make_data(_Kp, Type, 0) -> 
 4137:     odd_keys(Type);
 4138: make_data(1, set, I) ->
 4139:     [{I, q,w} | make_data(1, set, I-1)];
 4140: make_data(2, set, I) ->
 4141:     [{hh, I, q,w} | make_data(2, set, I-1)];
 4142: make_data(1, bag, I) ->
 4143:     [{I, q,w} , {I, hah, 77} | make_data(1, bag, I-1)];
 4144: make_data(2, bag, I) ->
 4145:     [{hh, I, q,w} , {hh, I, lalal, 900} | make_data(2, bag, I-1)];
 4146: make_data(1, duplicate_bag, I) ->
 4147:     [{I, q,w} , {I, hah, 77} | make_data(1, duplicate_bag, I-1)];
 4148: make_data(2, duplicate_bag, I) ->
 4149:     [{hh, I, q,w} , {hh, I, lalal, 900} | make_data(2, duplicate_bag, I-1)].
 4150: 
 4151: odd_keys(_) ->
 4152:     [{foo, 1 ,2},
 4153:      {{foo, foo}, 2,3},
 4154:      {"kakaka", {{{}}}, jj},
 4155:      {{"kallll", "kkk", []}, 66.7777},
 4156:      {make_ref(), 99, 66},
 4157:      {{1},2,3,4,5,6,7,duplicate(50, 8)},
 4158:      {self(), 7,8,88},
 4159:      {[self()], 8, 11}].
 4160: 
 4161: 
 4162: ins(_T, 0) ->
 4163:     ok;
 4164: ins(T, N) ->
 4165:     case dets:insert(T, {N, item(N)}) of
 4166: 	ok -> ins(T, N-1);
 4167: 	Error -> Error
 4168:     end.
 4169: 
 4170: item(N) when N rem 2 =:= 0 ->
 4171:     {item, number, N};
 4172: item(N) ->
 4173:     {item, number, N, a, much, bigger, one, i, think}.
 4174: 
 4175: del(_T, N, _I) when N =< 0 ->
 4176:     ok;
 4177: del(T, N, I) ->
 4178:     ok = dets:delete(T, N),
 4179:     del(T, N-I, I).
 4180: 
 4181: ensure_node(0, _Node) ->
 4182:     could_not_start_node;
 4183: ensure_node(N, Node) ->
 4184:     case net_adm:ping(Node) of
 4185: 	pang ->
 4186: 	    receive after 1000 ->
 4187: 			     ok
 4188: 		    end,
 4189: 	    ensure_node(N-1,Node);
 4190: 	pong ->
 4191: 	    ok
 4192:     end.
 4193: 
 4194: size_test(Len, Tabs) ->
 4195:     foreach(fun(Tab) ->
 4196:                     Len = dets:info(Tab, size)
 4197:             end, Tabs).
 4198: 
 4199: no_keys_test([T | Ts]) ->
 4200:     no_keys_test(T),
 4201:     no_keys_test(Ts);
 4202: no_keys_test([]) ->
 4203:     ok;
 4204: no_keys_test(T) ->
 4205:     case dets:info(T, version) of
 4206: 	8 ->
 4207: 	    ok;
 4208: 	9 ->
 4209: 	    Kp = dets:info(T, keypos),
 4210: 	    All = dets:match_object(T, '_'),
 4211: 	    L = lists:map(fun(X) -> element(Kp, X) end, All),
 4212: 	    NoKeys = length(lists:usort(L)),
 4213: 	    case {dets:info(T, no_keys), NoKeys} of
 4214:                 {N, N} ->
 4215:                     ok;
 4216:                 {N1, N2} ->
 4217:                     exit({no_keys_test, N1, N2})
 4218:             end
 4219:     end.
 4220: 
 4221: safe_get_all_objects(Tab) ->
 4222:     dets:safe_fixtable(Tab, true),
 4223:     Objects = get_all_objects(Tab),
 4224:     dets:safe_fixtable(Tab, false),
 4225:     Objects.
 4226: 
 4227: %% Caution: unless the table has been fixed, strange results can be returned.
 4228: get_all_objects(Tab) -> get_all_objects(dets:first(Tab), Tab, []).
 4229: 
 4230: %% Assuming no key matches {error, Reason}...
 4231: get_all_objects('$end_of_table', _Tab, L) -> L;
 4232: get_all_objects({error, Reason}, _Tab, _L) ->
 4233:     exit({get_all_objects, {error, Reason}});
 4234: get_all_objects(Key, Tab, L) -> 
 4235:     Objs = dets:lookup(Tab, Key),
 4236:     get_all_objects(dets:next(Tab, Key), Tab, Objs ++ L).
 4237: 
 4238: count_objects_quite_fast(Tab) ->
 4239:     R1 = dets:match_object(Tab, '_', 1),
 4240:     count_objs_1(R1, 0).
 4241: 
 4242: count_objs_1('$end_of_table', N) ->
 4243:     N;
 4244: count_objs_1({Ts,C}, N) when is_list(Ts) ->
 4245:     count_objs_1(dets:match_object(C), length(Ts) + N).
 4246: 
 4247: get_all_objects_fast(Tab) ->
 4248:     dets:match_object(Tab, '_').
 4249: 
 4250: %% Relevant for version 8.
 4251: histogram(Tab) ->
 4252:     OnePercent = case dets:info(Tab, no_slots) of
 4253: 	undefined -> undefined;
 4254: 	{_, NoSlots, _} -> NoSlots/100
 4255:     end,
 4256:     histogram(Tab, OnePercent).
 4257: 
 4258: histogram(Tab, OnePercent) ->
 4259:     E = ets:new(histo, []),
 4260:     dets:safe_fixtable(Tab, true),
 4261:     Hist = histo(Tab, E, 0, OnePercent, OnePercent),
 4262:     dets:safe_fixtable(Tab, false),
 4263:     case Hist of
 4264:         ok ->
 4265:             H = ets:tab2list(E),
 4266:             true = ets:delete(E),
 4267:             sort(H);
 4268:         Error ->
 4269:             ets:delete(E),
 4270:             Error
 4271:     end.
 4272: 
 4273: histo(T, E, I, One, Count) when is_number(Count), I > Count ->
 4274:     io:format("."),
 4275:     histo(T, E, I, One, Count+One);
 4276: histo(T, E, I, One, Count) ->
 4277:     case dets:slot(T, I) of
 4278:         '$end_of_table' when is_number(Count) ->
 4279:             io:format("~n"),
 4280:             ok;
 4281:         '$end_of_table' ->
 4282:             ok;
 4283:         Objs when is_list(Objs) ->
 4284:             L = length(Objs),
 4285:             case catch ets:update_counter(E, L, 1) of
 4286:                 {'EXIT', _} ->
 4287:                     ets:insert(E, {L, 1});
 4288:                 _ ->
 4289:                     ok
 4290:             end,
 4291:             histo(T, E, I+1, One, Count);
 4292:         Error ->
 4293:             Error
 4294:     end.
 4295: 
 4296: sum_histogram(H) ->
 4297:     sum_histogram(H, 0).
 4298: 
 4299: sum_histogram([{S,N1} | H], N) ->
 4300:     sum_histogram(H, N + S*N1);
 4301: sum_histogram([], N) ->
 4302:     N.
 4303: 
 4304: ave_histogram(H) ->
 4305:     ave_histogram(H, 0)/sum_histogram(H).
 4306: 
 4307: ave_histogram([{S,N1} | H], N) ->
 4308:     ave_histogram(H, N + S*S*N1);
 4309: ave_histogram([], N) ->
 4310:     N.
 4311: 
 4312: bad_object({error,{bad_object,FileName}}, FileName) ->
 4313:     ok; % Version 8, no debug.
 4314: bad_object({error,{{bad_object,_,_},FileName}}, FileName) ->
 4315:     ok; % Version 8, debug...
 4316: bad_object({error,{{bad_object,_}, FileName}}, FileName) ->
 4317:     ok; % No debug.
 4318: bad_object({error,{{{bad_object,_,_},_,_,_}, FileName}}, FileName) ->
 4319:     ok. % Debug.
 4320: 
 4321: check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) ->
 4322:     true;
 4323: check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args)  ->
 4324:     true = test_server:is_native(M) andalso length(Args) =:= A.
 4325: 
 4326: check_pps(P0) ->
 4327:     case pps() of
 4328:         P0 ->
 4329:             ok;
 4330:         _ ->
 4331:             %% On some (rare) occasions the dets process is still
 4332:             %% running although the call to close() has returned, as
 4333:             %% it seems...
 4334:             timer:sleep(500),
 4335:             case pps() of
 4336:                 P0 ->
 4337:                     ok;
 4338:                 P1 -> 
 4339:                     io:format("failure, got ~p~n, expected ~p\n", [P1, P0]),
 4340:                     {Ports0,Procs0} = P0,
 4341:                     {Ports1,Procs1} = P1,
 4342:                     show("Old ports", Ports0 -- Ports1),
 4343:                     show("New ports", Ports1 -- Ports0),
 4344:                     show("Old procs", Procs0 -- Procs1),
 4345:                     show("New procs", Procs1 -- Procs0),
 4346:                     ?t:fail()
 4347:             end
 4348:     end.
 4349: 
 4350: show(_S, []) ->
 4351:     ok;
 4352: show(S, L) ->
 4353:     io:format("~s: ~p~n", [S, L]).
 4354: 
 4355: pps() ->
 4356:     dets:start(),
 4357:     {port_list(), process_list()}.
 4358: 
 4359: port_list() ->
 4360:     [{P,safe_second_element(erlang:port_info(P, name))} || 
 4361:         P <- erlang:ports()].
 4362: 
 4363: process_list() ->
 4364:     [{P,process_info(P, registered_name),
 4365:       safe_second_element(process_info(P, initial_call))} || 
 4366:         P <- processes()].
 4367: 
 4368: safe_second_element({_,Info}) -> Info;
 4369: safe_second_element(Other) -> Other.