xref: /dragonfly/lib/libc/db/test/dbtest.c (revision 73610d44)
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