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.