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