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