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
main(int argc,char * argv[])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
compare(DBT * db1,DBT * db2)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
get(DB * dbp,DBT * kp)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
getdata(DB * dbp,DBT * kp,DBT * dp)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
put(DB * dbp,DBT * kp,DBT * dp)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
rem(DB * dbp,DBT * kp)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
synk(DB * dbp)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
seq(DB * dbp,DBT * kp)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
dump(DB * dbp,int rev)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
setflags(char * s)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 *
sflags(int flags)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
dbtype(char * s)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 *
setinfo(DBTYPE type,char * s)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 *
rfile(char * name,size_t * lenp)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 *
xmalloc(char * text,size_t len)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
usage(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
err(const char * fmt,...)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