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