1#!/bin/sh - 2# 3# @(#)run.test 8.13 (Berkeley) 11/2/95 4# 5 6. ./runenv.sh 7 8# db regression tests 9main() 10{ 11 12 PROG=./dbtest 13 TMP1=${TMPDIR-.}/t1 14 TMP2=${TMPDIR-.}/t2 15 TMP3=${TMPDIR-.}/t3 16 BINFILES=${TMPDIR-.}/binfiles 17 18 if [ \! -z "$WORDLIST" -a -f "$WORDLIST" ]; then 19 DICT=$WORDLIST 20 elif [ -f /usr/local/lib/dict/words ]; then 21 DICT=/usr/local/lib/dict/words 22 elif [ -f /usr/share/dict/words ]; then 23 DICT=/usr/share/dict/words 24 elif [ -f /usr/dict/words ]; then 25 DICT=/usr/dict/words 26 elif [ -f /usr/share/lib/dict/words ]; then 27 DICT=/usr/share/lib/dict/words 28 elif [ -f $srcdir/../test/dictionary ]; then 29 DICT=`cd $srcdir/../test && pwd`/dictionary 30 else 31 echo 'run.test: no dictionary' 32 exit 1 33 fi 34 35 dictsize=`wc -l < $DICT` 36 37 bindir=/bin/. 38 find $bindir -type f -exec test -r {} \; -print | head -100 > $BINFILES 39 40 if [ $# -eq 0 ]; then 41 for t in 1 2 3 4 5 6 7 8 9 10 11 12 13 20 40 41 50 60 61 62 63; do 42 test$t 43 done 44 else 45 while [ $# -gt 0 ] 46 do case "$1" in 47 test*) 48 $1;; 49 [0-9]*) 50 test$1;; 51 btree) 52 for t in 1 2 3 7 8 9 10 12 13 40 41 50 60 61 62 63; do 53 test$t 54 done;; 55 hash) 56 for t in 1 2 3 8 13 20; do 57 test$t 58 done;; 59 recno) 60 for t in 1 2 3 4 5 6 7 10 11; do 61 test$t 62 done;; 63 *) 64 echo "run.test: unknown test $1" 65 echo "usage: run.test test# | type" 66 exit 1 67 esac 68 shift 69 done 70 fi 71 rm -f $TMP1 $TMP2 $TMP3 $BINFILES 72 exit 0 73} 74 75getnwords() { 76 # Delete blank lines because the db code appears not to like 77 # empty keys. Omit lines with non-alphanumeric characters to 78 # avoid shell metacharacters and non-ASCII characters which 79 # could cause 'rev' to choke. 80 LC_ALL=C sed -e '/^$/d' -e '/[^A-Za-z]/d' < $DICT | sed -e ${1}q 81} 82 83# Take the first hundred entries in the dictionary, and make them 84# be key/data pairs. 85test1() 86{ 87 echo "Test 1: btree, hash: small key, small data pairs" 88 getnwords 200 > $TMP1 89 for type in btree hash; do 90 rm -f $TMP2 $TMP3 91 for i in `cat $TMP1`; do 92 echo p 93 echo k$i 94 echo d$i 95 echo g 96 echo k$i 97 done > $TMP2 98 $PROG -o $TMP3 $type $TMP2 99 if (cmp -s $TMP1 $TMP3) ; then : 100 else 101 echo "test1: type $type: failed" 102 exit 1 103 fi 104 done 105 echo "Test 1: recno: small key, small data pairs" 106 rm -f $TMP2 $TMP3 107 awk '{ 108 ++i; 109 printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); 110 }' < $TMP1 > $TMP2 111 $PROG -o $TMP3 recno $TMP2 112 if (cmp -s $TMP1 $TMP3) ; then : 113 else 114 echo "test1: type recno: failed" 115 exit 1 116 fi 117} 118 119# Take the first 200 entries in the dictionary, and give them 120# each a medium size data entry. 121test2() 122{ 123 echo "Test 2: btree, hash: small key, medium data pairs" 124 mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz 125 echo $mdata | 126 awk '{ for (i = 1; i < 201; ++i) print $0 }' > $TMP1 127 for type in hash btree; do 128 rm -f $TMP2 $TMP3 129 for i in `getnwords 200`; do 130 echo p 131 echo k$i 132 echo d$mdata 133 echo g 134 echo k$i 135 done > $TMP2 136 $PROG -o $TMP3 $type $TMP2 137 if (cmp -s $TMP1 $TMP3) ; then : 138 else 139 echo "test2: type $type: failed" 140 exit 1 141 fi 142 done 143 echo "Test 2: recno: small key, medium data pairs" 144 rm -f $TMP2 $TMP3 145 echo $mdata | 146 awk '{ for (i = 1; i < 201; ++i) 147 printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); 148 }' > $TMP2 149 $PROG -o $TMP3 recno $TMP2 150 if (cmp -s $TMP1 $TMP3) ; then : 151 else 152 echo "test2: type recno: failed" 153 exit 1 154 fi 155} 156 157# Insert the programs in $bindir with their paths as their keys. 158test3() 159{ 160 echo "Test 3: hash: small key, big data pairs" 161 rm -f $TMP1 162 xargs cat < $BINFILES > $TMP1 163 for type in hash; do 164 rm -f $TMP2 $TMP3 165 for i in `cat $BINFILES`; do 166 echo p 167 echo k$i 168 echo D$i 169 echo g 170 echo k$i 171 done > $TMP2 172 $PROG -o $TMP3 $type $TMP2 173 if (cmp -s $TMP1 $TMP3) ; then : 174 else 175 echo "test3: $type: failed" 176 exit 1 177 fi 178 done 179 echo "Test 3: btree: small key, big data pairs" 180 for psize in 512 16384 65536; do 181 echo " page size $psize" 182 for type in btree; do 183 rm -f $TMP2 $TMP3 184 for i in `cat $BINFILES`; do 185 echo p 186 echo k$i 187 echo D$i 188 echo g 189 echo k$i 190 done > $TMP2 191 $PROG -i psize=$psize -o $TMP3 $type $TMP2 192 if (cmp -s $TMP1 $TMP3) ; then : 193 else 194 echo "test3: $type: page size $psize: failed" 195 exit 1 196 fi 197 done 198 done 199 echo "Test 3: recno: big data pairs" 200 rm -f $TMP2 $TMP3 201 awk '{ 202 ++i; 203 printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i); 204 }' < $BINFILES > $TMP2 205 for psize in 512 16384 65536; do 206 echo " page size $psize" 207 $PROG -i psize=$psize -o $TMP3 recno $TMP2 208 if (cmp -s $TMP1 $TMP3) ; then : 209 else 210 echo "test3: recno: page size $psize: failed" 211 exit 1 212 fi 213 done 214} 215 216# Do random recno entries. 217test4() 218{ 219 echo "Test 4: recno: random entries" 220 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 221 awk '{ 222 for (i = 37; i <= 37 + 88 * 17; i += 17) { 223 if (i % 41) 224 s = substr($0, 1, i % 41); 225 else 226 s = substr($0, 1); 227 printf("input key %d: %s\n", i, s); 228 } 229 for (i = 1; i <= 15; ++i) { 230 if (i % 41) 231 s = substr($0, 1, i % 41); 232 else 233 s = substr($0, 1); 234 printf("input key %d: %s\n", i, s); 235 } 236 for (i = 19234; i <= 19234 + 61 * 27; i += 27) { 237 if (i % 41) 238 s = substr($0, 1, i % 41); 239 else 240 s = substr($0, 1); 241 printf("input key %d: %s\n", i, s); 242 } 243 exit 244 }' > $TMP1 245 rm -f $TMP2 $TMP3 246 cat $TMP1 | 247 awk 'BEGIN { 248 i = 37; 249 incr = 17; 250 } 251 { 252 printf("p\nk%d\nd%s\n", i, $0); 253 if (i == 19234 + 61 * 27) 254 exit; 255 if (i == 37 + 88 * 17) { 256 i = 1; 257 incr = 1; 258 } else if (i == 15) { 259 i = 19234; 260 incr = 27; 261 } else 262 i += incr; 263 } 264 END { 265 for (i = 37; i <= 37 + 88 * 17; i += 17) 266 printf("g\nk%d\n", i); 267 for (i = 1; i <= 15; ++i) 268 printf("g\nk%d\n", i); 269 for (i = 19234; i <= 19234 + 61 * 27; i += 27) 270 printf("g\nk%d\n", i); 271 }' > $TMP2 272 $PROG -o $TMP3 recno $TMP2 273 if (cmp -s $TMP1 $TMP3) ; then : 274 else 275 echo "test4: type recno: failed" 276 exit 1 277 fi 278} 279 280# Do reverse order recno entries. 281test5() 282{ 283 echo "Test 5: recno: reverse order entries" 284 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 285 awk ' { 286 for (i = 1500; i; --i) { 287 if (i % 34) 288 s = substr($0, 1, i % 34); 289 else 290 s = substr($0, 1); 291 printf("input key %d: %s\n", i, s); 292 } 293 exit; 294 }' > $TMP1 295 rm -f $TMP2 $TMP3 296 cat $TMP1 | 297 awk 'BEGIN { 298 i = 1500; 299 } 300 { 301 printf("p\nk%d\nd%s\n", i, $0); 302 --i; 303 } 304 END { 305 for (i = 1500; i; --i) 306 printf("g\nk%d\n", i); 307 }' > $TMP2 308 $PROG -o $TMP3 recno $TMP2 309 if (cmp -s $TMP1 $TMP3) ; then : 310 else 311 echo "test5: type recno: failed" 312 exit 1 313 fi 314} 315 316# Do alternating order recno entries. 317test6() 318{ 319 echo "Test 6: recno: alternating order entries" 320 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 321 awk ' { 322 for (i = 1; i < 1200; i += 2) { 323 if (i % 34) 324 s = substr($0, 1, i % 34); 325 else 326 s = substr($0, 1); 327 printf("input key %d: %s\n", i, s); 328 } 329 for (i = 2; i < 1200; i += 2) { 330 if (i % 34) 331 s = substr($0, 1, i % 34); 332 else 333 s = substr($0, 1); 334 printf("input key %d: %s\n", i, s); 335 } 336 exit; 337 }' > $TMP1 338 rm -f $TMP2 $TMP3 339 cat $TMP1 | 340 awk 'BEGIN { 341 i = 1; 342 even = 0; 343 } 344 { 345 printf("p\nk%d\nd%s\n", i, $0); 346 i += 2; 347 if (i >= 1200) { 348 if (even == 1) 349 exit; 350 even = 1; 351 i = 2; 352 } 353 } 354 END { 355 for (i = 1; i < 1200; ++i) 356 printf("g\nk%d\n", i); 357 }' > $TMP2 358 $PROG -o $TMP3 recno $TMP2 359 sort -o $TMP1 $TMP1 360 sort -o $TMP3 $TMP3 361 if (cmp -s $TMP1 $TMP3) ; then : 362 else 363 echo "test6: type recno: failed" 364 exit 1 365 fi 366} 367 368# Delete cursor record 369test7() 370{ 371 echo "Test 7: btree, recno: delete cursor record" 372 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 373 awk '{ 374 for (i = 1; i <= 120; ++i) 375 printf("%05d: input key %d: %s\n", i, i, $0); 376 printf("%05d: input key %d: %s\n", 120, 120, $0); 377 printf("seq failed, no such key\n"); 378 printf("%05d: input key %d: %s\n", 1, 1, $0); 379 printf("%05d: input key %d: %s\n", 2, 2, $0); 380 exit; 381 }' > $TMP1 382 rm -f $TMP2 $TMP3 383 384 for type in btree recno; do 385 cat $TMP1 | 386 awk '{ 387 if (i == 120) 388 exit; 389 printf("p\nk%d\nd%s\n", ++i, $0); 390 } 391 END { 392 printf("fR_NEXT\n"); 393 for (i = 1; i <= 120; ++i) 394 printf("s\n"); 395 printf("fR_CURSOR\ns\nk120\n"); 396 printf("r\n"); 397 printf("fR_NEXT\ns\n"); 398 printf("fR_CURSOR\ns\nk1\n"); 399 printf("r\n"); 400 printf("fR_FIRST\ns\n"); 401 }' > $TMP2 402 $PROG -o $TMP3 recno $TMP2 403 if (cmp -s $TMP1 $TMP3) ; then : 404 else 405 echo "test7: type $type: failed" 406 exit 1 407 fi 408 done 409} 410 411# Make sure that overflow pages are reused. 412test8() 413{ 414 echo "Test 8: btree: repeated small key, big data pairs" 415 rm -f $TMP1 416 # /bin/csh is no longer ubiquitous - find a substitute 417 # The test stores contents of a known file 418 tfile="" 419 for tp in /bin/csh /bin/ls /usr/bin/ls /bin/cat /usr/bin/cat; do 420 if [ "x$tfile" = "x" -a -f $tp ]; then 421 tfile=$tp 422 fi 423 done 424 if [ "x$tfile" = "x" ]; then 425 echo "No suitable file for testing purposes" 426 exit 1 427 fi 428 echo "" | 429 awk 'BEGIN { 430 for (i = 1; i <= 10; ++i) { 431 printf("p\nkkey1\nD/bin/sh\n"); 432 printf("p\nkkey2\nD'$tfile'\n"); 433 if (i % 8 == 0) { 434 printf("c\nkkey2\nD'$tfile'\n"); 435 printf("c\nkkey1\nD/bin/sh\n"); 436 printf("e\t%d of 10 (comparison)\n", i); 437 } else 438 printf("e\t%d of 10 \n", i); 439 printf("r\nkkey1\nr\nkkey2\n"); 440 } 441 }' > $TMP1 442 if $PROG btree $TMP1 ; then 443 true 444 else 445 echo "test8: btree tests failed" 446 exit 1 447 fi 448# $PROG hash $TMP1 449 # No explicit test for success. 450} 451 452# Test btree duplicate keys 453test9() 454{ 455 echo "Test 9: btree: duplicate keys" 456 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 457 awk '{ 458 for (i = 1; i <= 543; ++i) 459 printf("%05d: input key %d: %s\n", i, i, $0); 460 exit; 461 }' > $TMP1 462 rm -f $TMP2 $TMP3 463 464 for type in btree; do 465 cat $TMP1 | 466 awk '{ 467 if (i++ % 2) 468 printf("p\nkduplicatekey\nd%s\n", $0); 469 else 470 printf("p\nkunique%dkey\nd%s\n", i, $0); 471 } 472 END { 473 printf("o\n"); 474 }' > $TMP2 475 $PROG -iflags=1 -o $TMP3 $type $TMP2 476 sort -o $TMP3 $TMP3 477 if (cmp -s $TMP1 $TMP3) ; then : 478 else 479 echo "test9: type $type: failed" 480 exit 1 481 fi 482 done 483} 484 485# Test use of cursor flags without initialization 486test10() 487{ 488 echo "Test 10: btree, recno: test cursor flag use" 489 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 490 awk '{ 491 for (i = 1; i <= 20; ++i) 492 printf("%05d: input key %d: %s\n", i, i, $0); 493 exit; 494 }' > $TMP1 495 rm -f $TMP2 $TMP3 496 497 # Test that R_CURSOR doesn't succeed before cursor initialized 498 for type in btree recno; do 499 cat $TMP1 | 500 awk '{ 501 if (i == 10) 502 exit; 503 printf("p\nk%d\nd%s\n", ++i, $0); 504 } 505 END { 506 printf("fR_CURSOR\nr\n"); 507 printf("eR_CURSOR SHOULD HAVE FAILED\n"); 508 }' > $TMP2 509 $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 510 if [ -s $TMP3 ] ; then 511 echo "Test 10: delete: R_CURSOR SHOULD HAVE FAILED" 512 exit 1 513 fi 514 done 515 for type in btree recno; do 516 cat $TMP1 | 517 awk '{ 518 if (i == 10) 519 exit; 520 printf("p\nk%d\nd%s\n", ++i, $0); 521 } 522 END { 523 printf("fR_CURSOR\np\nk1\ndsome data\n"); 524 printf("eR_CURSOR SHOULD HAVE FAILED\n"); 525 }' > $TMP2 526 $PROG -o $TMP3 $type $TMP2 > /dev/null 2>&1 527 if [ -s $TMP3 ] ; then 528 echo "Test 10: put: R_CURSOR SHOULD HAVE FAILED" 529 exit 1 530 fi 531 done 532} 533 534# Test insert in reverse order. 535test11() 536{ 537 echo "Test 11: recno: reverse order insert" 538 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 539 awk '{ 540 for (i = 1; i <= 779; ++i) 541 printf("%05d: input key %d: %s\n", i, i, $0); 542 exit; 543 }' > $TMP1 544 rm -f $TMP2 $TMP3 545 546 for type in recno; do 547 cat $TMP1 | 548 awk '{ 549 if (i == 0) { 550 i = 1; 551 printf("p\nk1\nd%s\n", $0); 552 printf("%s\n", "fR_IBEFORE"); 553 } else 554 printf("p\nk1\nd%s\n", $0); 555 } 556 END { 557 printf("or\n"); 558 }' > $TMP2 559 $PROG -o $TMP3 $type $TMP2 560 if (cmp -s $TMP1 $TMP3) ; then : 561 else 562 echo "test11: type $type: failed" 563 exit 1 564 fi 565 done 566} 567 568# Take the first 20000 entries in the dictionary, reverse them, and give 569# them each a small size data entry. Use a small page size to make sure 570# the btree split code gets hammered. 571test12() 572{ 573 if ( rev < /dev/null ) > /dev/null 2>&1 ; then 574 : 575 else 576 echo "Test 12: skipped, rev not found" 577 return 578 fi 579 if test $dictsize -lt 20001 ; then 580 echo "Test 12: skipped, dictionary too small" 581 return 582 else 583 : 584 fi 585 echo "Test 12: btree: lots of keys, small page size" 586 mdata=abcdefghijklmnopqrstuvwxy 587 echo $mdata | 588 awk '{ for (i = 1; i < 20001; ++i) print $0 }' > $TMP1 589 for type in btree; do 590 rm -f $TMP2 $TMP3 591 for i in `getnwords 20000 | rev`; do 592 echo p 593 echo k$i 594 echo d$mdata 595 echo g 596 echo k$i 597 done > $TMP2 598 $PROG -i psize=512 -o $TMP3 $type $TMP2 599 if (cmp -s $TMP1 $TMP3) ; then : 600 else 601 echo "test12: type $type: failed" 602 exit 1 603 fi 604 done 605} 606 607# Test different byte orders. 608test13() 609{ 610 echo "Test 13: btree, hash: differing byte orders" 611 getnwords 50 > $TMP1 612 for order in 1234 4321; do 613 for type in btree hash; do 614 rm -f byte.file $TMP2 $TMP3 615 for i in `cat $TMP1`; do 616 echo p 617 echo k$i 618 echo d$i 619 echo S 620 echo g 621 echo k$i 622 done > $TMP2 623 $PROG -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 624 if (cmp -s $TMP1 $TMP3) ; then : 625 else 626 echo "test13: $type/$order put failed" 627 exit 1 628 fi 629 for i in `cat $TMP1`; do 630 echo g 631 echo k$i 632 done > $TMP2 633 $PROG -s \ 634 -ilorder=$order -f byte.file -o $TMP3 $type $TMP2 635 if (cmp -s $TMP1 $TMP3) ; then : 636 else 637 echo "test13: $type/$order get failed" 638 exit 1 639 fi 640 done 641 done 642 rm -f byte.file 643} 644 645# Try a variety of bucketsizes and fill factors for hashing 646test20() 647{ 648 if test $dictsize -lt 10001 ; then 649 echo "Test 20: skipped, dictionary too small" 650 return 651 else 652 : 653 fi 654 echo\ 655 "Test 20: hash: bucketsize, fill factor; nelem 25000 cachesize 65536" 656 echo "abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" | 657 awk '{ 658 for (i = 1; i <= 10000; ++i) { 659 if (i % 34) 660 s = substr($0, 1, i % 34); 661 else 662 s = substr($0, 1); 663 printf("%s\n", s); 664 } 665 exit; 666 }' > $TMP1 667 getnwords 10000 | 668 awk 'BEGIN { 669 ds="abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg" 670 } 671 { 672 if (++i % 34) 673 s = substr(ds, 1, i % 34); 674 else 675 s = substr(ds, 1); 676 printf("p\nk%s\nd%s\n", $0, s); 677 }' > $TMP2 678 getnwords 10000 | 679 awk '{ 680 ++i; 681 printf("g\nk%s\n", $0); 682 }' >> $TMP2 683 bsize=256 684 for ffactor in 11 14 21; do 685 echo " bucketsize $bsize, fill factor $ffactor" 686 $PROG -o$TMP3 \ 687 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 688 hash $TMP2 689 if (cmp -s $TMP1 $TMP3) ; then : 690 else 691 echo "test20: type hash:\ 692bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 693 exit 1 694 fi 695 done 696 bsize=512 697 for ffactor in 21 28 43; do 698 echo " bucketsize $bsize, fill factor $ffactor" 699 $PROG -o$TMP3 \ 700 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 701 hash $TMP2 702 if (cmp -s $TMP1 $TMP3) ; then : 703 else 704 echo "test20: type hash:\ 705bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 706 exit 1 707 fi 708 done 709 bsize=1024 710 for ffactor in 43 57 85; do 711 echo " bucketsize $bsize, fill factor $ffactor" 712 $PROG -o$TMP3 \ 713 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 714 hash $TMP2 715 if (cmp -s $TMP1 $TMP3) ; then : 716 else 717 echo "test20: type hash:\ 718bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 719 exit 1 720 fi 721 done 722 bsize=2048 723 for ffactor in 85 114 171; do 724 echo " bucketsize $bsize, fill factor $ffactor" 725 $PROG -o$TMP3 \ 726 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 727 hash $TMP2 728 if (cmp -s $TMP1 $TMP3) ; then : 729 else 730 echo "test20: type hash:\ 731bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 732 exit 1 733 fi 734 done 735 bsize=4096 736 for ffactor in 171 228 341; do 737 echo " bucketsize $bsize, fill factor $ffactor" 738 $PROG -o$TMP3 \ 739 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 740 hash $TMP2 741 if (cmp -s $TMP1 $TMP3) ; then : 742 else 743 echo "test20: type hash:\ 744bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 745 exit 1 746 fi 747 done 748 bsize=8192 749 for ffactor in 341 455 683; do 750 echo " bucketsize $bsize, fill factor $ffactor" 751 $PROG -o$TMP3 \ 752 -ibsize=$bsize,ffactor=$ffactor,nelem=25000,cachesize=65536\ 753 hash $TMP2 754 if (cmp -s $TMP1 $TMP3) ; then : 755 else 756 echo "test20: type hash:\ 757bsize=$bsize ffactor=$ffactor nelem=25000 cachesize=65536 failed" 758 exit 1 759 fi 760 done 761} 762 763# Test for a weird page split condition where an insertion into index 764# 0 of a page that would cause the new item to be the only item on the 765# left page results in index 0 of the right page being erroneously 766# skipped; this only happens with one particular key+data length for 767# each page size. 768test40 () { 769 echo "Test 40: btree: page split on index 0" 770 e=: 771 for psize in 512 1024 2048 4096 8192; do 772 echo " page size $psize" 773 kdsizes=`awk 'BEGIN { 774 psize = '$psize'; hsize = int(psize/2); 775 for (kdsize = hsize-40; kdsize <= hsize; kdsize++) { 776 print kdsize; 777 } 778 }' /dev/null` 779 780 # Use a series of keylen+datalen values in the right 781 # neighborhood to find the one that triggers the bug. 782 # We could compute the exact size that triggers the 783 # bug but this additional fuzz may be useful. 784 785 # Insert keys in reverse order to maximize the chances 786 # for a split on index 0. 787 788 for kdsize in $kdsizes; do 789 awk 'BEGIN { 790 kdsize = '$kdsize'; 791 for (i = 8; i-- > 0; ) { 792 s = sprintf("a%03d:%09d", i, kdsize); 793 for (j = 0; j < kdsize-20; j++) { 794 s = s "x"; 795 } 796 printf("p\nka%03d\nd%s\n", i, s); 797 } 798 print "o"; 799 }' /dev/null > $TMP2 800 sed -n 's/^d//p' $TMP2 | sort > $TMP1 801 $PROG -o $TMP3 -i psize=$psize btree $TMP2 802 if (cmp -s $TMP1 $TMP3); then : 803 else 804 echo "test40: btree: page size $psize, \ 805keylen+datalen=$kdsize failed" 806 e='exit 1' 807 fi 808 done 809 done 810 $e 811} 812 813# Extremely tricky test attempting to replicate some unusual database 814# corruption seen in the field: pieces of the database becoming 815# inaccessible to random access, sequential access, or both. The 816# hypothesis is that at least some of these are triggered by the bug 817# in page splits on index 0 with a particular exact keylen+datalen. 818# (See Test 40.) For psize=4096, this size is exactly 2024. 819 820# The order of operations here relies on very specific knowledge of 821# the internals of the btree access method in order to place records 822# at specific offsets in a page and to create certain keys on internal 823# pages. The to-be-split page immediately prior to the bug-triggering 824# split has the following properties: 825# 826# * is not the leftmost leaf page 827# * key on the parent page is compares less than the key of the item 828# on index 0 829# * triggering record's key also compares greater than the key on the 830# parent page 831 832# Additionally, we prime the mpool LRU chain so that the head page on 833# the chain has the following properties: 834# 835# * record at index 0 is located where it will not get overwritten by 836# items written to the right-hand page during the split 837# * key of the record at index 0 compares less than the key of the 838# bug-triggering record 839 840# If the page-split bug exists, this test appears to create a database 841# where some records are inaccessible to a search, but still remain in 842# the file and are accessible by sequential traversal. At least one 843# record gets duplicated out of sequence. 844 845test41 () { 846 echo "Test 41: btree: no unsearchables due to page split on index 0" 847 # list of individual retrievals in a variable for easy reuse 848 list=`(for i in a b c d; do 849 for j in 990 998 999; do 850 echo g ${i}${j} 1024 851 done 852 done; 853 echo g y997 2014 854 for i in y z; do 855 for j in 998 999; do 856 echo g ${i}${j} 1024 857 done 858 done)` 859 # Exact number for trigger condition accounts for newlines 860 # retained by dbtest with -ofile but not without; we use 861 # -ofile, so count newlines. keylen=5,datalen=5+2014 for 862 # psize=4096 here. 863 (cat - <<EOF 864p z999 1024 865p z998 1024 866p y999 1024 867p y990 1024 868p d999 1024 869p d990 1024 870p c999 1024 871p c990 1024 872p b999 1024 873p b990 1024 874p a999 1024 875p a990 1024 876p y998 1024 877r y990 878p d998 1024 879p d990 1024 880p c998 1024 881p c990 1024 882p b998 1024 883p b990 1024 884p a998 1024 885p a990 1024 886p y997 2014 887S 888o 889EOF 890 echo "$list") | 891 # awk script input: 892 # {p|g|r} key [datasize] 893 awk '/^[pgr]/{ 894 printf("%s\nk%s\n", $1, $2); 895 } 896 /^p/{ 897 s = $2; 898 for (i = 0; i < $3; i++) { 899 s = s "x"; 900 } 901 printf("d%s\n", s); 902 } 903 !/^[pgr]/{ 904 print $0; 905 }' > $TMP2 906 (echo "$list"; echo "$list") | awk '{ 907 s = $2; 908 for (i = 0; i < $3; i++) { 909 s = s "x"; 910 } 911 print s; 912 }' > $TMP1 913 $PROG -o $TMP3 -i psize=4096 btree $TMP2 914 if (cmp -s $TMP1 $TMP3); then : 915 else 916 echo "test41: btree: failed" 917 exit 1 918 fi 919} 920 921# Test for recursive traversal successfully retrieving records that 922# are inaccessible to normal sequential (sibling-link) traversal. 923# This works by unlinking a few leaf pages but leaving their parent 924# links intact. To verify that the unlink actually makes records 925# inaccessible, the test first uses "o" to do a normal sequential 926# traversal, followed by "O" to do a recursive traversal. 927test50 () { 928 echo "Test 50: btree: recursive traversal" 929 fill="abcdefghijklmnopqrstuvwxyzy" 930 script='{ 931 for (i = 0; i < 20000; i++) { 932 printf("p\nkAA%05d\nd%05d%s\n", i, i, $0); 933 } 934 print "u"; 935 print "u"; 936 print "u"; 937 print "u"; 938 }' 939 (echo $fill | awk "$script"; echo o) > $TMP2 940 echo $fill | 941 awk '{ 942 for (i = 0; i < 20000; i++) { 943 printf("%05d%s\n", i, $0); 944 } 945 }' > $TMP1 946 $PROG -o $TMP3 -i psize=512 btree $TMP2 947 if (cmp -s $TMP1 $TMP3); then 948 echo "test50: btree: unexpected success after unlinking pages" 949 exit 1 950 fi 951 (echo $fill | awk "$script"; echo O) > $TMP2 952 $PROG -o $TMP3 -i psize=512 btree $TMP2 953 if (cmp -s $TMP1 $TMP3); then : 954 else 955 echo "test50: btree: failed" 956 exit 1 957 fi 958} 959 960test60 () { 961 echo "Test 60: btree: big key, small data, byteswap unaligned access" 962 # 488 = 512 - 20 (header) - 3 ("foo") - 1 (newline) 963 (echo foo; echo bar) | 964 awk '{ 965 s = $0 966 for (i = 0; i < 488; i++) { 967 s = s "x"; 968 } 969 printf("p\nk%s\ndx\n", s); 970 }' > $TMP2 971 for order in 1234 4321; do 972 $PROG -o $TMP3 -i psize=512,lorder=$order btree $TMP2 973 done 974} 975 976test61 () { 977 echo "Test 61: btree: small key, big data, byteswap unaligned access" 978 # 484 = 512 - 20 (header) - 7 ("foo1234") - 1 (newline) 979 (echo foo1234; echo bar1234) | 980 awk '{ 981 s = $0 982 for (i = 0; i < 484; i++) { 983 s = s "x"; 984 } 985 printf("p\nk%s\nd%s\n", $0, s); 986 }' > $TMP2 987 for order in 1234 4321; do 988 $PROG -o $TMP3 -i psize=512,lorder=$order btree $TMP2 989 done 990} 991 992test62 () { 993 echo "Test 62: btree: small key, big data, known byte order files" 994 (echo foo1234; echo bar1234) | 995 awk '{ 996 s = $0 997 for (i = 0; i < 484; i++) { 998 s = s "x"; 999 } 1000 printf("%s\n", s); 1001 }' > $TMP1 1002 (echo g; echo kfoo1234; echo g; echo kbar1234) > $TMP2 1003 for f in t.le.db t.be.db; do 1004 echo " $f" 1005 $PROG -f $f -s -o $TMP3 btree $TMP2 1006 if (cmp -s $TMP1 $TMP3); then : 1007 else 1008 echo "test62: btree: failed" 1009 exit 1 1010 fi 1011 done 1012} 1013 1014test63 () { 1015 echo "Test 63: btree: big key, medium data, bt_split unaligned access" 1016 # 488 = 512 - 20 (header) - 3 ("foo") - 1 (newline) 1017 # 223 = 232 - 8 (key pointer) 1018 # 232 = bt_ovflsize = (512 - 20 (header)) / 2 (DEFMINKEYPAGE) 1019 # - (2 (indx_t) + 12 (NBLEAFDBT(0,0))) 1020 awk 'BEGIN { 1021 s = ""; 1022 for (i = 0; i < 488; i++) { 1023 s = s "x"; 1024 } 1025 d = ""; 1026 for (i = 0; i < 223; i++) { 1027 d = d "x"; 1028 } 1029 for (i = 0; i < 128; i++) { 1030 printf("p\nk%s%03d\nd%s\n", s, i, d); 1031 } 1032 }' /dev/null > $TMP2 1033 $PROG -o $TMP3 -i psize=512 btree $TMP2 1034} 1035 1036main $* 1037