1 /* $OpenBSD: dbtest.c,v 1.6 2001/01/29 02:05:40 niklas Exp $ */ 2 /* $NetBSD: dbtest.c,v 1.8 1996/05/03 21:57:48 cgd Exp $ */ 3 4 /*- 5 * Copyright (c) 1992, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char copyright[] = 39 "@(#) Copyright (c) 1992, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 #if 0 45 static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; 46 #else 47 static char rcsid[] = "$OpenBSD: dbtest.c,v 1.6 2001/01/29 02:05:40 niklas Exp $"; 48 #endif 49 #endif /* not lint */ 50 51 #include <sys/param.h> 52 #include <sys/stat.h> 53 54 #include <ctype.h> 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <limits.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 63 #include <db.h> 64 65 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; 66 67 void compare __P((DBT *, DBT *)); 68 DBTYPE dbtype __P((char *)); 69 void dump __P((DB *, int)); 70 void err __P((const char *, ...)); 71 void get __P((DB *, DBT *)); 72 void getdata __P((DB *, DBT *, DBT *)); 73 void put __P((DB *, DBT *, DBT *)); 74 void rem __P((DB *, DBT *)); 75 char *sflags __P((int)); 76 void synk __P((DB *)); 77 void *rfile __P((char *, size_t *)); 78 void seq __P((DB *, DBT *)); 79 u_int setflags __P((char *)); 80 void *setinfo __P((DBTYPE, char *)); 81 void usage __P((void)); 82 void *xmalloc __P((char *, size_t)); 83 84 DBTYPE type; /* Database type. */ 85 void *infop; /* Iflags. */ 86 u_long lineno; /* Current line in test script. */ 87 u_int flags; /* Current DB flags. */ 88 int ofd = STDOUT_FILENO; /* Standard output fd. */ 89 90 DB *XXdbp; /* Global for gdb. */ 91 int XXlineno; /* Fast breakpoint for gdb. */ 92 93 int 94 main(argc, argv) 95 int argc; 96 char *argv[]; 97 { 98 extern int optind; 99 extern char *optarg; 100 enum S command, state; 101 DB *dbp; 102 DBT data, key, keydata; 103 size_t len; 104 int ch, oflags, sflag; 105 char *fname, *infoarg, *p, *t, buf[8 * 1024]; 106 107 infoarg = NULL; 108 fname = NULL; 109 oflags = O_CREAT | O_RDWR; 110 sflag = 0; 111 while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1) 112 switch (ch) { 113 case 'f': 114 fname = optarg; 115 break; 116 case 'i': 117 infoarg = optarg; 118 break; 119 case 'l': 120 oflags |= DB_LOCK; 121 break; 122 case 'o': 123 if ((ofd = open(optarg, 124 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) 125 err("%s: %s", optarg, strerror(errno)); 126 break; 127 case 's': 128 sflag = 1; 129 break; 130 case '?': 131 default: 132 usage(); 133 } 134 argc -= optind; 135 argv += optind; 136 137 if (argc != 2) 138 usage(); 139 140 /* Set the type. */ 141 type = dbtype(*argv++); 142 143 /* Open the descriptor file. */ 144 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) 145 err("%s: %s", *argv, strerror(errno)); 146 147 /* Set up the db structure as necessary. */ 148 if (infoarg == NULL) 149 infop = NULL; 150 else 151 for (p = strtok(infoarg, ",\t "); p != NULL; 152 p = strtok(0, ",\t ")) 153 if (*p != '\0') 154 infop = setinfo(type, p); 155 156 /* 157 * Open the DB. Delete any preexisting copy, you almost never 158 * want it around, and it often screws up tests. 159 */ 160 if (fname == NULL) { 161 p = getenv("TMPDIR"); 162 if (p == NULL) 163 p = "/var/tmp"; 164 (void)sprintf(buf, "%s/__dbtest", p); 165 fname = buf; 166 (void)unlink(buf); 167 } else if (!sflag) 168 (void)unlink(fname); 169 170 if ((dbp = dbopen(fname, 171 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) 172 err("dbopen: %s", strerror(errno)); 173 XXdbp = dbp; 174 175 state = COMMAND; 176 for (lineno = 1; 177 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { 178 /* Delete the newline, displaying the key/data is easier. */ 179 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) 180 *t = '\0'; 181 if ((len = strlen(buf)) == 0 || isspace(*p) || *p == '#') 182 continue; 183 184 /* Convenient gdb break point. */ 185 if (XXlineno == lineno) 186 XXlineno = 1; 187 switch (*p) { 188 case 'c': /* compare */ 189 if (state != COMMAND) 190 err("line %lu: not expecting command", lineno); 191 state = KEY; 192 command = COMPARE; 193 break; 194 case 'e': /* echo */ 195 if (state != COMMAND) 196 err("line %lu: not expecting command", lineno); 197 /* Don't display the newline, if CR at EOL. */ 198 if (p[len - 2] == '\r') 199 --len; 200 if (write(ofd, p + 1, len - 1) != len - 1 || 201 write(ofd, "\n", 1) != 1) 202 err("write: %s", strerror(errno)); 203 break; 204 case 'g': /* get */ 205 if (state != COMMAND) 206 err("line %lu: not expecting command", lineno); 207 state = KEY; 208 command = GET; 209 break; 210 case 'p': /* put */ 211 if (state != COMMAND) 212 err("line %lu: not expecting command", lineno); 213 state = KEY; 214 command = PUT; 215 break; 216 case 'r': /* remove */ 217 if (state != COMMAND) 218 err("line %lu: not expecting command", lineno); 219 if (flags == R_CURSOR) { 220 rem(dbp, &key); 221 state = COMMAND; 222 } else { 223 state = KEY; 224 command = REMOVE; 225 } 226 break; 227 case 'S': /* sync */ 228 if (state != COMMAND) 229 err("line %lu: not expecting command", lineno); 230 synk(dbp); 231 state = COMMAND; 232 break; 233 case 's': /* seq */ 234 if (state != COMMAND) 235 err("line %lu: not expecting command", lineno); 236 if (flags == R_CURSOR) { 237 state = KEY; 238 command = SEQ; 239 } else 240 seq(dbp, &key); 241 break; 242 case 'f': 243 flags = setflags(p + 1); 244 break; 245 case 'D': /* data file */ 246 if (state != DATA) 247 err("line %lu: not expecting data", lineno); 248 data.data = rfile(p + 1, &data.size); 249 goto ldata; 250 case 'd': /* data */ 251 if (state != DATA) 252 err("line %lu: not expecting data", lineno); 253 data.data = xmalloc(p + 1, len - 1); 254 data.size = len - 1; 255 ldata: switch (command) { 256 case COMPARE: 257 compare(&keydata, &data); 258 break; 259 case PUT: 260 put(dbp, &key, &data); 261 break; 262 default: 263 err("line %lu: command doesn't take data", 264 lineno); 265 } 266 if (type != DB_RECNO) 267 free(key.data); 268 free(data.data); 269 state = COMMAND; 270 break; 271 case 'K': /* key file */ 272 if (state != KEY) 273 err("line %lu: not expecting a key", lineno); 274 if (type == DB_RECNO) 275 err("line %lu: 'K' not available for recno", 276 lineno); 277 key.data = rfile(p + 1, &key.size); 278 goto lkey; 279 case 'k': /* key */ 280 if (state != KEY) 281 err("line %lu: not expecting a key", lineno); 282 if (type == DB_RECNO) { 283 static recno_t recno; 284 recno = atoi(p + 1); 285 key.data = &recno; 286 key.size = sizeof(recno); 287 } else { 288 key.data = xmalloc(p + 1, len - 1); 289 key.size = len - 1; 290 } 291 lkey: switch (command) { 292 case COMPARE: 293 getdata(dbp, &key, &keydata); 294 state = DATA; 295 break; 296 case GET: 297 get(dbp, &key); 298 if (type != DB_RECNO) 299 free(key.data); 300 state = COMMAND; 301 break; 302 case PUT: 303 state = DATA; 304 break; 305 case REMOVE: 306 rem(dbp, &key); 307 if ((type != DB_RECNO) && (flags != R_CURSOR)) 308 free(key.data); 309 state = COMMAND; 310 break; 311 case SEQ: 312 seq(dbp, &key); 313 if ((type != DB_RECNO) && (flags != R_CURSOR)) 314 free(key.data); 315 state = COMMAND; 316 break; 317 default: 318 err("line %lu: command doesn't take a key", 319 lineno); 320 } 321 break; 322 case 'o': 323 dump(dbp, p[1] == 'r'); 324 break; 325 default: 326 err("line %lu: %s: unknown command character", 327 lineno, p); 328 } 329 } 330 #ifdef STATISTICS 331 /* 332 * -l must be used (DB_LOCK must be set) for this to be 333 * used, otherwise a page will be locked and it will fail. 334 */ 335 if (type == DB_BTREE && oflags & DB_LOCK) 336 __bt_stat(dbp); 337 #endif 338 if (dbp->close(dbp)) 339 err("db->close: %s", strerror(errno)); 340 (void)close(ofd); 341 exit(0); 342 } 343 344 #define NOOVERWRITE "put failed, would overwrite key\n" 345 346 void 347 compare(db1, db2) 348 DBT *db1, *db2; 349 { 350 register size_t len; 351 register u_char *p1, *p2; 352 353 if (db1->size != db2->size) 354 printf("compare failed: key->data len %lu != data len %lu\n", 355 db1->size, db2->size); 356 357 len = MIN(db1->size, db2->size); 358 for (p1 = db1->data, p2 = db2->data; len--;) 359 if (*p1++ != *p2++) { 360 printf("compare failed at offset %d\n", 361 p1 - (u_char *)db1->data); 362 break; 363 } 364 } 365 366 void 367 get(dbp, kp) 368 DB *dbp; 369 DBT *kp; 370 { 371 DBT data; 372 373 switch (dbp->get(dbp, kp, &data, flags)) { 374 case 0: 375 (void)write(ofd, data.data, data.size); 376 if (ofd == STDOUT_FILENO) 377 (void)write(ofd, "\n", 1); 378 break; 379 case -1: 380 err("line %lu: get: %s", lineno, strerror(errno)); 381 /* NOTREACHED */ 382 case 1: 383 #define NOSUCHKEY "get failed, no such key\n" 384 if (ofd != STDOUT_FILENO) 385 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 386 else 387 (void)fprintf(stderr, "%d: %.*s: %s", 388 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 389 #undef NOSUCHKEY 390 break; 391 } 392 } 393 394 void 395 getdata(dbp, kp, dp) 396 DB *dbp; 397 DBT *kp, *dp; 398 { 399 switch (dbp->get(dbp, kp, dp, flags)) { 400 case 0: 401 return; 402 case -1: 403 err("line %lu: getdata: %s", lineno, strerror(errno)); 404 /* NOTREACHED */ 405 case 1: 406 err("line %lu: getdata failed, no such key", lineno); 407 /* NOTREACHED */ 408 } 409 } 410 411 void 412 put(dbp, kp, dp) 413 DB *dbp; 414 DBT *kp, *dp; 415 { 416 switch (dbp->put(dbp, kp, dp, flags)) { 417 case 0: 418 break; 419 case -1: 420 err("line %lu: put: %s", lineno, strerror(errno)); 421 /* NOTREACHED */ 422 case 1: 423 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); 424 break; 425 } 426 } 427 428 void 429 rem(dbp, kp) 430 DB *dbp; 431 DBT *kp; 432 { 433 switch (dbp->del(dbp, kp, flags)) { 434 case 0: 435 break; 436 case -1: 437 err("line %lu: rem: %s", lineno, strerror(errno)); 438 /* NOTREACHED */ 439 case 1: 440 #define NOSUCHKEY "rem failed, no such key\n" 441 if (ofd != STDOUT_FILENO) 442 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 443 else if (flags != R_CURSOR) 444 (void)fprintf(stderr, "%d: %.*s: %s", 445 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 446 else 447 (void)fprintf(stderr, 448 "%d: rem of cursor failed\n", lineno); 449 #undef NOSUCHKEY 450 break; 451 } 452 } 453 454 void 455 synk(dbp) 456 DB *dbp; 457 { 458 switch (dbp->sync(dbp, flags)) { 459 case 0: 460 break; 461 case -1: 462 err("line %lu: synk: %s", lineno, strerror(errno)); 463 /* NOTREACHED */ 464 } 465 } 466 467 void 468 seq(dbp, kp) 469 DB *dbp; 470 DBT *kp; 471 { 472 DBT data; 473 474 switch (dbp->seq(dbp, kp, &data, flags)) { 475 case 0: 476 (void)write(ofd, data.data, data.size); 477 if (ofd == STDOUT_FILENO) 478 (void)write(ofd, "\n", 1); 479 break; 480 case -1: 481 err("line %lu: seq: %s", lineno, strerror(errno)); 482 /* NOTREACHED */ 483 case 1: 484 #define NOSUCHKEY "seq failed, no such key\n" 485 if (ofd != STDOUT_FILENO) 486 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 487 else if (flags == R_CURSOR) 488 (void)fprintf(stderr, "%d: %.*s: %s", 489 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY); 490 else 491 (void)fprintf(stderr, 492 "%d: seq (%s) failed\n", lineno, sflags(flags)); 493 #undef NOSUCHKEY 494 break; 495 } 496 } 497 498 void 499 dump(dbp, rev) 500 DB *dbp; 501 int rev; 502 { 503 DBT key, data; 504 int flags, nflags; 505 506 if (rev) { 507 flags = R_LAST; 508 nflags = R_PREV; 509 } else { 510 flags = R_FIRST; 511 nflags = R_NEXT; 512 } 513 for (;; flags = nflags) 514 switch (dbp->seq(dbp, &key, &data, flags)) { 515 case 0: 516 (void)write(ofd, data.data, data.size); 517 if (ofd == STDOUT_FILENO) 518 (void)write(ofd, "\n", 1); 519 break; 520 case 1: 521 goto done; 522 case -1: 523 err("line %lu: (dump) seq: %s", 524 lineno, strerror(errno)); 525 /* NOTREACHED */ 526 } 527 done: return; 528 } 529 530 u_int 531 setflags(s) 532 char *s; 533 { 534 char *p; 535 536 for (; isspace(*s); ++s); 537 if (*s == '\n' || *s == '\0') 538 return (0); 539 if ((p = strchr(s, '\n')) != NULL) 540 *p = '\0'; 541 if (!strcmp(s, "R_CURSOR")) return (R_CURSOR); 542 if (!strcmp(s, "R_FIRST")) return (R_FIRST); 543 if (!strcmp(s, "R_IAFTER")) return (R_IAFTER); 544 if (!strcmp(s, "R_IBEFORE")) return (R_IBEFORE); 545 if (!strcmp(s, "R_LAST")) return (R_LAST); 546 if (!strcmp(s, "R_NEXT")) return (R_NEXT); 547 if (!strcmp(s, "R_NOOVERWRITE")) return (R_NOOVERWRITE); 548 if (!strcmp(s, "R_PREV")) return (R_PREV); 549 if (!strcmp(s, "R_SETCURSOR")) return (R_SETCURSOR); 550 551 err("line %lu: %s: unknown flag", lineno, s); 552 /* NOTREACHED */ 553 } 554 555 char * 556 sflags(flags) 557 int flags; 558 { 559 switch (flags) { 560 case R_CURSOR: return ("R_CURSOR"); 561 case R_FIRST: return ("R_FIRST"); 562 case R_IAFTER: return ("R_IAFTER"); 563 case R_IBEFORE: return ("R_IBEFORE"); 564 case R_LAST: return ("R_LAST"); 565 case R_NEXT: return ("R_NEXT"); 566 case R_NOOVERWRITE: return ("R_NOOVERWRITE"); 567 case R_PREV: return ("R_PREV"); 568 case R_SETCURSOR: return ("R_SETCURSOR"); 569 } 570 571 return ("UNKNOWN!"); 572 } 573 574 DBTYPE 575 dbtype(s) 576 char *s; 577 { 578 if (!strcmp(s, "btree")) 579 return (DB_BTREE); 580 if (!strcmp(s, "hash")) 581 return (DB_HASH); 582 if (!strcmp(s, "recno")) 583 return (DB_RECNO); 584 err("%s: unknown type (use btree, hash or recno)", s); 585 /* NOTREACHED */ 586 } 587 588 void * 589 setinfo(type, s) 590 DBTYPE type; 591 char *s; 592 { 593 static BTREEINFO ib; 594 static HASHINFO ih; 595 static RECNOINFO rh; 596 char *eq; 597 598 if ((eq = strchr(s, '=')) == NULL) 599 err("%s: illegal structure set statement", s); 600 *eq++ = '\0'; 601 if (!isdigit(*eq)) 602 err("%s: structure set statement must be a number", s); 603 604 switch (type) { 605 case DB_BTREE: 606 if (!strcmp("flags", s)) { 607 ib.flags = atoi(eq); 608 return (&ib); 609 } 610 if (!strcmp("cachesize", s)) { 611 ib.cachesize = atoi(eq); 612 return (&ib); 613 } 614 if (!strcmp("maxkeypage", s)) { 615 ib.maxkeypage = atoi(eq); 616 return (&ib); 617 } 618 if (!strcmp("minkeypage", s)) { 619 ib.minkeypage = atoi(eq); 620 return (&ib); 621 } 622 if (!strcmp("lorder", s)) { 623 ib.lorder = atoi(eq); 624 return (&ib); 625 } 626 if (!strcmp("psize", s)) { 627 ib.psize = atoi(eq); 628 return (&ib); 629 } 630 break; 631 case DB_HASH: 632 if (!strcmp("bsize", s)) { 633 ih.bsize = atoi(eq); 634 return (&ih); 635 } 636 if (!strcmp("ffactor", s)) { 637 ih.ffactor = atoi(eq); 638 return (&ih); 639 } 640 if (!strcmp("nelem", s)) { 641 ih.nelem = atoi(eq); 642 return (&ih); 643 } 644 if (!strcmp("cachesize", s)) { 645 ih.cachesize = atoi(eq); 646 return (&ih); 647 } 648 if (!strcmp("lorder", s)) { 649 ih.lorder = atoi(eq); 650 return (&ih); 651 } 652 break; 653 case DB_RECNO: 654 if (!strcmp("flags", s)) { 655 rh.flags = atoi(eq); 656 return (&rh); 657 } 658 if (!strcmp("cachesize", s)) { 659 rh.cachesize = atoi(eq); 660 return (&rh); 661 } 662 if (!strcmp("lorder", s)) { 663 rh.lorder = atoi(eq); 664 return (&rh); 665 } 666 if (!strcmp("reclen", s)) { 667 rh.reclen = atoi(eq); 668 return (&rh); 669 } 670 if (!strcmp("bval", s)) { 671 rh.bval = atoi(eq); 672 return (&rh); 673 } 674 if (!strcmp("psize", s)) { 675 rh.psize = atoi(eq); 676 return (&rh); 677 } 678 break; 679 } 680 err("%s: unknown structure value", s); 681 /* NOTREACHED */ 682 } 683 684 void * 685 rfile(name, lenp) 686 char *name; 687 size_t *lenp; 688 { 689 struct stat sb; 690 void *p; 691 int fd; 692 char *np; 693 694 for (; isspace(*name); ++name); 695 if ((np = strchr(name, '\n')) != NULL) 696 *np = '\0'; 697 if ((fd = open(name, O_RDONLY, 0)) < 0 || 698 fstat(fd, &sb)) 699 err("%s: %s\n", name, strerror(errno)); 700 #ifdef NOT_PORTABLE 701 if (sb.st_size > (off_t)SIZE_T_MAX) 702 err("%s: %s\n", name, strerror(E2BIG)); 703 #endif 704 if ((p = (void *)malloc((u_int)sb.st_size)) == NULL) 705 err("%s", strerror(errno)); 706 (void)read(fd, p, (int)sb.st_size); 707 *lenp = sb.st_size; 708 (void)close(fd); 709 return (p); 710 } 711 712 void * 713 xmalloc(text, len) 714 char *text; 715 size_t len; 716 { 717 void *p; 718 719 if ((p = (void *)malloc(len)) == NULL) 720 err("%s", strerror(errno)); 721 memmove(p, text, len); 722 return (p); 723 } 724 725 void 726 usage() 727 { 728 (void)fprintf(stderr, 729 "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n"); 730 exit(1); 731 } 732 733 #ifdef __STDC__ 734 #include <stdarg.h> 735 #else 736 #include <varargs.h> 737 #endif 738 739 void 740 #ifdef __STDC__ 741 err(const char *fmt, ...) 742 #else 743 err(fmt, va_alist) 744 char *fmt; 745 va_dcl 746 #endif 747 { 748 va_list ap; 749 #ifdef __STDC__ 750 va_start(ap, fmt); 751 #else 752 va_start(ap); 753 #endif 754 (void)fprintf(stderr, "dbtest: "); 755 (void)vfprintf(stderr, fmt, ap); 756 va_end(ap); 757 (void)fprintf(stderr, "\n"); 758 exit(1); 759 /* NOTREACHED */ 760 } 761