1 /* $NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $ */
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
42 __RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $");
46 #include <sys/param.h>
62 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
64 static void compare(DBT *, DBT *);
65 static DBTYPE dbtype(const char *);
66 static void dump(DB *, int, int);
67 static void get(DB *, DBT *);
68 static void getdata(DB *, DBT *, DBT *);
69 static void put(DB *, DBT *, DBT *);
70 static void rem(DB *, DBT *);
71 static const char *sflags(int);
72 static void synk(DB *);
73 static void *rfile(char *, size_t *);
74 static void seq(DB *, DBT *);
75 static u_int setflags(char *);
76 static void *setinfo(DBTYPE, char *);
78 static void unlinkpg(DB *);
80 static void usage(void) __attribute__((__noreturn__));
81 static void *xcopy(void *, size_t);
82 static void chkcmd(enum S);
83 static void chkdata(enum S);
84 static void chkkey(enum S);
87 extern void __bt_stat(DB *);
90 extern int __bt_relink(BTREE *, PAGE *);
93 static DBTYPE type; /* Database type. */
94 static void *infop; /* Iflags. */
95 static size_t lineno; /* Current line in test script. */
96 static u_int flags; /* Current DB flags. */
97 static int ofd = STDOUT_FILENO; /* Standard output fd. */
99 static DB *XXdbp; /* Global for gdb. */
100 static size_t XXlineno; /* Fast breakpoint for gdb. */
103 main(int argc, char *argv[])
107 enum S command = COMMAND, state;
109 DBT data, key, keydata;
111 int ch, oflags, sflag;
112 char *fname, *infoarg, *p, *t, buf[8 * 1024];
117 unlink_dbfile = false;
118 oflags = O_CREAT | O_RDWR;
120 while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
132 if ((ofd = open(optarg,
133 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
134 err(1, "Cannot create `%s'", optarg);
150 type = dbtype(*argv++);
152 /* Open the descriptor file. */
153 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
154 err(1, "Cannot reopen `%s'", *argv);
156 /* Set up the db structure as necessary. */
160 for (p = strtok(infoarg, ",\t "); p != NULL;
161 p = strtok(0, ",\t "))
163 infop = setinfo(type, p);
166 * Open the DB. Delete any preexisting copy, you almost never
167 * want it around, and it often screws up tests.
170 const char *q = getenv("TMPDIR");
173 (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
176 unlink_dbfile = true;
180 if ((dbp = dbopen(fname,
181 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
182 err(1, "Cannot dbopen `%s'", fname);
189 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
190 /* Delete the newline, displaying the key/data is easier. */
191 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
193 if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
197 /* Convenient gdb break point. */
198 if (XXlineno == lineno)
201 case 'c': /* compare */
208 /* Don't display the newline, if CR at EOL. */
209 if (p[len - 2] == '\r')
211 if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
212 write(ofd, "\n", 1) != 1)
213 err(1, "write failed");
225 case 'r': /* remove */
227 if (flags == R_CURSOR) {
242 if (flags == R_CURSOR) {
249 flags = setflags(p + 1);
251 case 'D': /* data file */
253 data.data = rfile(p + 1, &data.size);
257 data.data = xcopy(p + 1, len - 1);
259 ldata: switch (command) {
261 compare(&keydata, &data);
264 put(dbp, &key, &data);
267 errx(1, "line %zu: command doesn't take data",
270 if (type != DB_RECNO)
275 case 'K': /* key file */
277 if (type == DB_RECNO)
278 errx(1, "line %zu: 'K' not available for recno",
280 key.data = rfile(p + 1, &key.size);
284 if (type == DB_RECNO) {
285 static recno_t recno;
288 key.size = sizeof(recno);
290 key.data = xcopy(p + 1, len - 1);
293 lkey: switch (command) {
295 getdata(dbp, &key, &keydata);
300 if (type != DB_RECNO)
309 if ((type != DB_RECNO) && (flags != R_CURSOR))
315 if ((type != DB_RECNO) && (flags != R_CURSOR))
320 errx(1, "line %zu: command doesn't take a key",
325 dump(dbp, p[1] == 'r', 0);
329 dump(dbp, p[1] == 'r', 1);
336 errx(1, "line %zu: %s: unknown command character",
342 * -l must be used (DB_LOCK must be set) for this to be
343 * used, otherwise a page will be locked and it will fail.
345 if (type == DB_BTREE && oflags & DB_LOCK)
348 if ((*dbp->close)(dbp))
349 err(1, "db->close failed");
354 #define NOOVERWRITE "put failed, would overwrite key\n"
357 compare(DBT *db1, DBT *db2)
362 if (db1->size != db2->size)
363 printf("compare failed: key->data len %zu != data len %zu\n",
364 db1->size, db2->size);
366 len = MIN(db1->size, db2->size);
367 for (p1 = db1->data, p2 = db2->data; len--;)
368 if (*p1++ != *p2++) {
369 printf("compare failed at offset %lu\n",
370 (unsigned long)(p1 - (u_char *)db1->data));
376 get(DB *dbp, DBT *kp)
380 switch ((*dbp->get)(dbp, kp, &data, flags)) {
382 (void)write(ofd, data.data, data.size);
383 if (ofd == STDOUT_FILENO)
384 (void)write(ofd, "\n", 1);
387 err(1, "line %zu: get failed", lineno);
390 #define NOSUCHKEY "get failed, no such key\n"
391 if (ofd != STDOUT_FILENO)
392 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
394 (void)fprintf(stderr, "%zu: %.*s: %s",
395 lineno, (int)MIN(kp->size, 20),
396 (const char *)kp->data,
404 getdata(DB *dbp, DBT *kp, DBT *dp)
406 switch ((*dbp->get)(dbp, kp, dp, flags)) {
410 err(1, "line %zu: getdata failed", lineno);
413 errx(1, "line %zu: getdata failed, no such key", lineno);
419 put(DB *dbp, DBT *kp, DBT *dp)
421 switch ((*dbp->put)(dbp, kp, dp, flags)) {
425 err(1, "line %zu: put failed", lineno);
428 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
434 rem(DB *dbp, DBT *kp)
436 switch ((*dbp->del)(dbp, kp, flags)) {
440 err(1, "line %zu: rem failed", lineno);
443 #define NOSUCHKEY "rem failed, no such key\n"
444 if (ofd != STDOUT_FILENO)
445 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
446 else if (flags != R_CURSOR)
447 (void)fprintf(stderr, "%zu: %.*s: %s",
448 lineno, (int)MIN(kp->size, 20),
449 (const char *)kp->data, NOSUCHKEY);
451 (void)fprintf(stderr,
452 "%zu: rem of cursor failed\n", lineno);
461 switch ((*dbp->sync)(dbp, flags)) {
465 err(1, "line %zu: synk failed", lineno);
471 seq(DB *dbp, DBT *kp)
475 switch (dbp->seq(dbp, kp, &data, flags)) {
477 (void)write(ofd, data.data, data.size);
478 if (ofd == STDOUT_FILENO)
479 (void)write(ofd, "\n", 1);
482 err(1, "line %zu: seq failed", lineno);
485 #define NOSUCHKEY "seq failed, no such key\n"
486 if (ofd != STDOUT_FILENO)
487 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
488 else if (flags == R_CURSOR)
489 (void)fprintf(stderr, "%zu: %.*s: %s",
490 lineno, (int)MIN(kp->size, 20),
491 (const char *)kp->data, NOSUCHKEY);
493 (void)fprintf(stderr,
494 "%zu: seq (%s) failed\n", lineno, sflags(flags));
501 dump(DB *dbp, int rev, int recurse)
509 nflags = recurse ? R_RPREV : R_PREV;
516 nflags = recurse ? R_RNEXT : R_NEXT;
521 for (;; xflags = nflags)
522 switch (dbp->seq(dbp, &key, &data, xflags)) {
524 (void)write(ofd, data.data, data.size);
525 if (ofd == STDOUT_FILENO)
526 (void)write(ofd, "\n", 1);
531 err(1, "line %zu: (dump) seq failed", lineno);
541 BTREE *t = dbp->internal;
545 for (pg = P_ROOT; pg < t->bt_mp->npages;
546 mpool_put(t->bt_mp, h, 0), pg++) {
547 if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
549 /* Look for a nonempty leaf page that has both left
550 * and right siblings. */
551 if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
553 if (NEXTINDEX(h) == 0)
555 if ((h->flags & (P_BLEAF | P_RLEAF)))
558 if (h == NULL || pg == t->bt_mp->npages) {
559 errx(1, "%s: no appropriate page found", __func__);
562 if (__bt_relink(t, h) != 0) {
566 h->prevpg = P_INVALID;
567 h->nextpg = P_INVALID;
569 mpool_put(t->bt_mp, h, MPOOL_DIRTY);
578 for (; isspace((unsigned char)*s); ++s);
579 if (*s == '\n' || *s == '\0')
581 if ((p = strchr(s, '\n')) != NULL)
583 if (!strcmp(s, "R_CURSOR")) return R_CURSOR;
584 if (!strcmp(s, "R_FIRST")) return R_FIRST;
585 if (!strcmp(s, "R_IAFTER")) return R_IAFTER;
586 if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE;
587 if (!strcmp(s, "R_LAST")) return R_LAST;
588 if (!strcmp(s, "R_NEXT")) return R_NEXT;
589 if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE;
590 if (!strcmp(s, "R_PREV")) return R_PREV;
591 if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR;
593 errx(1, "line %zu: %s: unknown flag", lineno, s);
601 case R_CURSOR: return "R_CURSOR";
602 case R_FIRST: return "R_FIRST";
603 case R_IAFTER: return "R_IAFTER";
604 case R_IBEFORE: return "R_IBEFORE";
605 case R_LAST: return "R_LAST";
606 case R_NEXT: return "R_NEXT";
607 case R_NOOVERWRITE: return "R_NOOVERWRITE";
608 case R_PREV: return "R_PREV";
609 case R_SETCURSOR: return "R_SETCURSOR";
616 dbtype(const char *s)
618 if (!strcmp(s, "btree"))
620 if (!strcmp(s, "hash"))
622 if (!strcmp(s, "recno"))
624 errx(1, "%s: unknown type (use btree, hash or recno)", s);
629 setinfo(DBTYPE dtype, char *s)
636 if ((eq = strchr(s, '=')) == NULL)
637 errx(1, "%s: illegal structure set statement", s);
639 if (!isdigit((unsigned char)*eq))
640 errx(1, "%s: structure set statement must be a number", s);
644 if (!strcmp("flags", s)) {
648 if (!strcmp("cachesize", s)) {
649 ib.cachesize = atoi(eq);
652 if (!strcmp("maxkeypage", s)) {
653 ib.maxkeypage = atoi(eq);
656 if (!strcmp("minkeypage", s)) {
657 ib.minkeypage = atoi(eq);
660 if (!strcmp("lorder", s)) {
661 ib.lorder = atoi(eq);
664 if (!strcmp("psize", s)) {
670 if (!strcmp("bsize", s)) {
674 if (!strcmp("ffactor", s)) {
675 ih.ffactor = atoi(eq);
678 if (!strcmp("nelem", s)) {
682 if (!strcmp("cachesize", s)) {
683 ih.cachesize = atoi(eq);
686 if (!strcmp("lorder", s)) {
687 ih.lorder = atoi(eq);
692 if (!strcmp("flags", s)) {
696 if (!strcmp("cachesize", s)) {
697 rh.cachesize = atoi(eq);
700 if (!strcmp("lorder", s)) {
701 rh.lorder = atoi(eq);
704 if (!strcmp("reclen", s)) {
705 rh.reclen = atoi(eq);
708 if (!strcmp("bval", s)) {
712 if (!strcmp("psize", s)) {
718 errx(1, "%s: unknown structure value", s);
723 rfile(char *name, size_t *lenp)
730 for (; isspace((unsigned char)*name); ++name)
732 if ((np = strchr(name, '\n')) != NULL)
734 if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
735 err(1, "Cannot open `%s'", name);
737 if (sb.st_size > (off_t)SIZE_T_MAX) {
739 err("Cannot process `%s'", name);
742 if ((p = malloc((size_t)sb.st_size)) == NULL)
743 err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
744 if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
745 err(1, "read failed");
746 *lenp = (size_t)sb.st_size;
752 xcopy(void *text, size_t len)
756 if ((p = malloc(len)) == NULL)
757 err(1, "Cannot allocate %zu bytes", len);
758 (void)memmove(p, text, len);
765 if (state != COMMAND)
766 errx(1, "line %zu: not expecting command", lineno);
770 chkdata(enum S state)
773 errx(1, "line %zu: not expecting data", lineno);
780 errx(1, "line %zu: not expecting a key", lineno);
786 (void)fprintf(stderr,
788 "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] "
790 "Usage: %s [-l] [-f file] [-i info] [-o file] "
792 "type script\n", getprogname());