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