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