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