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