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