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