diff --git a/tests/lib/libc/Makefile b/tests/lib/libc/Makefile index f3fe59f0decb..542d8d8e6b30 100644 --- a/tests/lib/libc/Makefile +++ b/tests/lib/libc/Makefile @@ -1,9 +1,9 @@ -# $NetBSD: Makefile,v 1.23 2011/01/07 02:47:40 pgoyette Exp $ +# $NetBSD: Makefile,v 1.24 2011/01/07 15:05:57 pgoyette Exp $ .include .include -TESTS_SUBDIRS+= gen hash ieeefp setjmp stdlib stdio string ttyio +TESTS_SUBDIRS+= db gen hash ieeefp setjmp stdlib stdio string ttyio .if ${HAS_SSP} == "yes" TESTS_SUBDIRS+= ssp diff --git a/tests/lib/libc/db/Makefile b/tests/lib/libc/db/Makefile new file mode 100644 index 000000000000..2a146b7bcfce --- /dev/null +++ b/tests/lib/libc/db/Makefile @@ -0,0 +1,18 @@ +# $NetBSD: Makefile,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ + +.include + +TESTSDIR= ${TESTSBASE}/lib/libc/db + +TESTS_SH+= t_db + +BINDIR= ${TESTSDIR} +MKMAN= no + +PROGS+= h_db + +FILESDIR= ${TESTSDIR} + +FILES+= README + +.include diff --git a/tests/lib/libc/db/README b/tests/lib/libc/db/README new file mode 100644 index 000000000000..68f0ec7a1c1a --- /dev/null +++ b/tests/lib/libc/db/README @@ -0,0 +1,66 @@ +# $NetBSD: README,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ +# @(#)README 8.8 (Berkeley) 7/31/94 + +Fairly large files (the command files) are built in this directory during +the test runs, and even larger files (the database files) are created in +"/var/tmp". If the latter directory doesn't exist, set the environmental +variable TMPDIR to a directory where the files can be built. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +The script file consists of lines with an initial character which is +the command for that line, or an initial character indicating a key +or data entry for a previous command. + +Legal command characters are as follows: + +c: compare a record + + must be followed by [kK][dD]; the data value in the database + associated with the specified key is compared to the specified + data value. +e: echo a string + + writes out the rest of the line into the output file; if the + last character is not a carriage-return, a newline is appended. +f: set the flags for the next command + + no value zero's the flags +g: do a get command + + must be followed by [kK] + + writes out the retrieved data DBT. +o [r]: dump [reverse] + + dump the database out, if 'r' is set, in reverse order. +p: do a put command + + must be followed by [kK][dD] +r: do a del command + + must be followed by [kK] unless R_CURSOR flag set. +S: sync the database +s: do a seq command + + must be followed by [kK] if R_CURSOR flag set. + + writes out the retrieved data DBT. + +Legal key/data characters are as follows: + +D [file]: data file + + set the current data value to the contents of the file +d [data]: + + set the current key value to the contents of the line. +K [file]: key file + + set the current key value to the contents of the file +k [data]: + + set the current key value to the contents of the line. + +Blank lines, lines with leading white space, and lines with leading +hash marks (#) are ignored. + +Options to dbtest are as follows: + + -d: Set the DB_LOCK flag. + -f: Use the file argument as the database file. + -i: Use the rest of the argument to set elements in the info + structure. If the type is btree, then "-i cachesize=10240" + will set BTREEINFO.cachesize to 10240. + -o: The rest of the argument is the output file instead of + using stdout. + -s: Don't delete the database file before opening it, i.e. + use the database file from a previous run. + +Dbtest requires two arguments, the type of access "hash", "recno" +or "btree", and the script name or "-" to indicate stdin. diff --git a/tests/lib/libc/db/h_db.c b/tests/lib/libc/db/h_db.c new file mode 100644 index 000000000000..dfb13859d3f2 --- /dev/null +++ b/tests/lib/libc/db/h_db.c @@ -0,0 +1,731 @@ +/* $NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94"; +#else +__RCSID("$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA }; + +static void compare(DBT *, DBT *); +static DBTYPE dbtype(const char *); +static void dump(DB *, int); +static void get(DB *, DBT *); +static void getdata(DB *, DBT *, DBT *); +static void put(DB *, DBT *, DBT *); +static void rem(DB *, DBT *); +static const char *sflags(int); +static void synk(DB *); +static void *rfile(char *, size_t *); +static void seq(DB *, DBT *); +static u_int setflags(char *); +static void *setinfo(DBTYPE, char *); +static void usage(void) __attribute__((__noreturn__)); +static void *xcopy(void *, size_t); +static void chkcmd(enum S); +static void chkdata(enum S); +static void chkkey(enum S); + +#ifdef STATISTICS +extern void __bt_stat(DB *); +#endif + +static DBTYPE type; /* Database type. */ +static void *infop; /* Iflags. */ +static size_t lineno; /* Current line in test script. */ +static u_int flags; /* Current DB flags. */ +static int ofd = STDOUT_FILENO; /* Standard output fd. */ + +static DB *XXdbp; /* Global for gdb. */ +static size_t XXlineno; /* Fast breakpoint for gdb. */ + +int +main(int argc, char *argv[]) +{ + extern int optind; + extern char *optarg; + enum S command = COMMAND, state; + DB *dbp; + DBT data, key, keydata; + size_t len; + int ch, oflags, sflag; + char *fname, *infoarg, *p, *t, buf[8 * 1024]; + bool unlink_dbfile; + + infoarg = NULL; + fname = NULL; + unlink_dbfile = false; + oflags = O_CREAT | O_RDWR; + sflag = 0; + while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1) + switch (ch) { + case 'f': + fname = optarg; + break; + case 'i': + infoarg = optarg; + break; + case 'l': + oflags |= DB_LOCK; + break; + case 'o': + if ((ofd = open(optarg, + O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + err(1, "Cannot create `%s'", optarg); + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* Set the type. */ + type = dbtype(*argv++); + + /* Open the descriptor file. */ + if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL) + err(1, "Cannot reopen `%s'", *argv); + + /* Set up the db structure as necessary. */ + if (infoarg == NULL) + infop = NULL; + else + for (p = strtok(infoarg, ",\t "); p != NULL; + p = strtok(0, ",\t ")) + if (*p != '\0') + infop = setinfo(type, p); + + /* + * Open the DB. Delete any preexisting copy, you almost never + * want it around, and it often screws up tests. + */ + if (fname == NULL) { + const char *q = getenv("TMPDIR"); + if (q == NULL) + q = "/var/tmp"; + (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q); + fname = buf; + (void)unlink(buf); + unlink_dbfile = true; + } else if (!sflag) + (void)unlink(fname); + + if ((dbp = dbopen(fname, + oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL) + err(1, "Cannot dbopen `%s'", fname); + XXdbp = dbp; + if (unlink_dbfile) + (void)unlink(fname); + + state = COMMAND; + for (lineno = 1; + (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) { + /* Delete the newline, displaying the key/data is easier. */ + if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL) + *t = '\0'; + if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) || + *p == '#') + continue; + + /* Convenient gdb break point. */ + if (XXlineno == lineno) + XXlineno = 1; + switch (*p) { + case 'c': /* compare */ + chkcmd(state); + state = KEY; + command = COMPARE; + break; + case 'e': /* echo */ + chkcmd(state); + /* Don't display the newline, if CR at EOL. */ + if (p[len - 2] == '\r') + --len; + if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 || + write(ofd, "\n", 1) != 1) + err(1, "write failed"); + break; + case 'g': /* get */ + chkcmd(state); + state = KEY; + command = GET; + break; + case 'p': /* put */ + chkcmd(state); + state = KEY; + command = PUT; + break; + case 'r': /* remove */ + chkcmd(state); + if (flags == R_CURSOR) { + rem(dbp, &key); + state = COMMAND; + } else { + state = KEY; + command = REMOVE; + } + break; + case 'S': /* sync */ + chkcmd(state); + synk(dbp); + state = COMMAND; + break; + case 's': /* seq */ + chkcmd(state); + if (flags == R_CURSOR) { + state = KEY; + command = SEQ; + } else + seq(dbp, &key); + break; + case 'f': + flags = setflags(p + 1); + break; + case 'D': /* data file */ + chkdata(state); + data.data = rfile(p + 1, &data.size); + goto ldata; + case 'd': /* data */ + chkdata(state); + data.data = xcopy(p + 1, len - 1); + data.size = len - 1; +ldata: switch (command) { + case COMPARE: + compare(&keydata, &data); + break; + case PUT: + put(dbp, &key, &data); + break; + default: + errx(1, "line %zu: command doesn't take data", + lineno); + } + if (type != DB_RECNO) + free(key.data); + free(data.data); + state = COMMAND; + break; + case 'K': /* key file */ + chkkey(state); + if (type == DB_RECNO) + errx(1, "line %zu: 'K' not available for recno", + lineno); + key.data = rfile(p + 1, &key.size); + goto lkey; + case 'k': /* key */ + chkkey(state); + if (type == DB_RECNO) { + static recno_t recno; + recno = atoi(p + 1); + key.data = &recno; + key.size = sizeof(recno); + } else { + key.data = xcopy(p + 1, len - 1); + key.size = len - 1; + } +lkey: switch (command) { + case COMPARE: + getdata(dbp, &key, &keydata); + state = DATA; + break; + case GET: + get(dbp, &key); + if (type != DB_RECNO) + free(key.data); + state = COMMAND; + break; + case PUT: + state = DATA; + break; + case REMOVE: + rem(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + case SEQ: + seq(dbp, &key); + if ((type != DB_RECNO) && (flags != R_CURSOR)) + free(key.data); + state = COMMAND; + break; + default: + errx(1, "line %zu: command doesn't take a key", + lineno); + } + break; + case 'o': + dump(dbp, p[1] == 'r'); + break; + default: + errx(1, "line %zu: %s: unknown command character", + lineno, p); + } + } +#ifdef STATISTICS + /* + * -l must be used (DB_LOCK must be set) for this to be + * used, otherwise a page will be locked and it will fail. + */ + if (type == DB_BTREE && oflags & DB_LOCK) + __bt_stat(dbp); +#endif + if ((*dbp->close)(dbp)) + err(1, "db->close failed"); + (void)close(ofd); + return 0; +} + +#define NOOVERWRITE "put failed, would overwrite key\n" + +static void +compare(DBT *db1, DBT *db2) +{ + size_t len; + u_char *p1, *p2; + + if (db1->size != db2->size) + printf("compare failed: key->data len %zu != data len %zu\n", + db1->size, db2->size); + + len = MIN(db1->size, db2->size); + for (p1 = db1->data, p2 = db2->data; len--;) + if (*p1++ != *p2++) { + printf("compare failed at offset %lu\n", + (unsigned long)(p1 - (u_char *)db1->data)); + break; + } +} + +static void +get(DB *dbp, DBT *kp) +{ + DBT data; + + switch ((*dbp->get)(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err(1, "line %zu: get failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "get failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, + NOSUCHKEY); +#undef NOSUCHKEY + break; + } +} + +static void +getdata(DB *dbp, DBT *kp, DBT *dp) +{ + switch ((*dbp->get)(dbp, kp, dp, flags)) { + case 0: + return; + case -1: + err(1, "line %zu: getdata failed", lineno); + /* NOTREACHED */ + case 1: + errx(1, "line %zu: getdata failed, no such key", lineno); + /* NOTREACHED */ + } +} + +static void +put(DB *dbp, DBT *kp, DBT *dp) +{ + switch ((*dbp->put)(dbp, kp, dp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: put failed", lineno); + /* NOTREACHED */ + case 1: + (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); + break; + } +} + +static void +rem(DB *dbp, DBT *kp) +{ + switch ((*dbp->del)(dbp, kp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: rem failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "rem failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags != R_CURSOR) + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%zu: rem of cursor failed\n", lineno); +#undef NOSUCHKEY + break; + } +} + +static void +synk(DB *dbp) +{ + switch ((*dbp->sync)(dbp, flags)) { + case 0: + break; + case -1: + err(1, "line %zu: synk failed", lineno); + /* NOTREACHED */ + } +} + +static void +seq(DB *dbp, DBT *kp) +{ + DBT data; + + switch (dbp->seq(dbp, kp, &data, flags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case -1: + err(1, "line %zu: seq failed", lineno); + /* NOTREACHED */ + case 1: +#define NOSUCHKEY "seq failed, no such key\n" + if (ofd != STDOUT_FILENO) + (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); + else if (flags == R_CURSOR) + (void)fprintf(stderr, "%zu: %.*s: %s", + lineno, (int)MIN(kp->size, 20), + (const char *)kp->data, NOSUCHKEY); + else + (void)fprintf(stderr, + "%zu: seq (%s) failed\n", lineno, sflags(flags)); +#undef NOSUCHKEY + break; + } +} + +static void +dump(DB *dbp, int rev) +{ + DBT key, data; + int xflags, nflags; + + if (rev) { + xflags = R_LAST; + nflags = R_PREV; + } else { + xflags = R_FIRST; + nflags = R_NEXT; + } + for (;; xflags = nflags) + switch (dbp->seq(dbp, &key, &data, xflags)) { + case 0: + (void)write(ofd, data.data, data.size); + if (ofd == STDOUT_FILENO) + (void)write(ofd, "\n", 1); + break; + case 1: + goto done; + case -1: + err(1, "line %zu: (dump) seq failed", lineno); + /* NOTREACHED */ + } +done: return; +} + +static u_int +setflags(char *s) +{ + char *p; + + for (; isspace((unsigned char)*s); ++s); + if (*s == '\n' || *s == '\0') + return 0; + if ((p = strchr(s, '\n')) != NULL) + *p = '\0'; + if (!strcmp(s, "R_CURSOR")) return R_CURSOR; + if (!strcmp(s, "R_FIRST")) return R_FIRST; + if (!strcmp(s, "R_IAFTER")) return R_IAFTER; + if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE; + if (!strcmp(s, "R_LAST")) return R_LAST; + if (!strcmp(s, "R_NEXT")) return R_NEXT; + if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE; + if (!strcmp(s, "R_PREV")) return R_PREV; + if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR; + + errx(1, "line %zu: %s: unknown flag", lineno, s); + /* NOTREACHED */ +} + +static const char * +sflags(int xflags) +{ + switch (xflags) { + case R_CURSOR: return "R_CURSOR"; + case R_FIRST: return "R_FIRST"; + case R_IAFTER: return "R_IAFTER"; + case R_IBEFORE: return "R_IBEFORE"; + case R_LAST: return "R_LAST"; + case R_NEXT: return "R_NEXT"; + case R_NOOVERWRITE: return "R_NOOVERWRITE"; + case R_PREV: return "R_PREV"; + case R_SETCURSOR: return "R_SETCURSOR"; + } + + return "UNKNOWN!"; +} + +static DBTYPE +dbtype(const char *s) +{ + if (!strcmp(s, "btree")) + return DB_BTREE; + if (!strcmp(s, "hash")) + return DB_HASH; + if (!strcmp(s, "recno")) + return DB_RECNO; + errx(1, "%s: unknown type (use btree, hash or recno)", s); + /* NOTREACHED */ +} + +static void * +setinfo(DBTYPE dtype, char *s) +{ + static BTREEINFO ib; + static HASHINFO ih; + static RECNOINFO rh; + char *eq; + + if ((eq = strchr(s, '=')) == NULL) + errx(1, "%s: illegal structure set statement", s); + *eq++ = '\0'; + if (!isdigit((unsigned char)*eq)) + errx(1, "%s: structure set statement must be a number", s); + + switch (dtype) { + case DB_BTREE: + if (!strcmp("flags", s)) { + ib.flags = atoi(eq); + return &ib; + } + if (!strcmp("cachesize", s)) { + ib.cachesize = atoi(eq); + return &ib; + } + if (!strcmp("maxkeypage", s)) { + ib.maxkeypage = atoi(eq); + return &ib; + } + if (!strcmp("minkeypage", s)) { + ib.minkeypage = atoi(eq); + return &ib; + } + if (!strcmp("lorder", s)) { + ib.lorder = atoi(eq); + return &ib; + } + if (!strcmp("psize", s)) { + ib.psize = atoi(eq); + return &ib; + } + break; + case DB_HASH: + if (!strcmp("bsize", s)) { + ih.bsize = atoi(eq); + return &ih; + } + if (!strcmp("ffactor", s)) { + ih.ffactor = atoi(eq); + return &ih; + } + if (!strcmp("nelem", s)) { + ih.nelem = atoi(eq); + return &ih; + } + if (!strcmp("cachesize", s)) { + ih.cachesize = atoi(eq); + return &ih; + } + if (!strcmp("lorder", s)) { + ih.lorder = atoi(eq); + return &ih; + } + break; + case DB_RECNO: + if (!strcmp("flags", s)) { + rh.flags = atoi(eq); + return &rh; + } + if (!strcmp("cachesize", s)) { + rh.cachesize = atoi(eq); + return &rh; + } + if (!strcmp("lorder", s)) { + rh.lorder = atoi(eq); + return &rh; + } + if (!strcmp("reclen", s)) { + rh.reclen = atoi(eq); + return &rh; + } + if (!strcmp("bval", s)) { + rh.bval = atoi(eq); + return &rh; + } + if (!strcmp("psize", s)) { + rh.psize = atoi(eq); + return &rh; + } + break; + } + errx(1, "%s: unknown structure value", s); + /* NOTREACHED */ +} + +static void * +rfile(char *name, size_t *lenp) +{ + struct stat sb; + void *p; + int fd; + char *np; + + for (; isspace((unsigned char)*name); ++name) + continue; + if ((np = strchr(name, '\n')) != NULL) + *np = '\0'; + if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1) + err(1, "Cannot open `%s'", name); +#ifdef NOT_PORTABLE + if (sb.st_size > (off_t)SIZE_T_MAX) { + errno = E2BIG; + err("Cannot process `%s'", name); + } +#endif + if ((p = malloc((size_t)sb.st_size)) == NULL) + err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size); + if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size) + err(1, "read failed"); + *lenp = (size_t)sb.st_size; + (void)close(fd); + return p; +} + +static void * +xcopy(void *text, size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + err(1, "Cannot allocate %zu bytes", len); + (void)memmove(p, text, len); + return p; +} + +static void +chkcmd(enum S state) +{ + if (state != COMMAND) + errx(1, "line %zu: not expecting command", lineno); +} + +static void +chkdata(enum S state) +{ + if (state != DATA) + errx(1, "line %zu: not expecting data", lineno); +} + +static void +chkkey(enum S state) +{ + if (state != KEY) + errx(1, "line %zu: not expecting a key", lineno); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "Usage: %s [-l] [-f file] [-i info] [-o file] type script\n", + getprogname()); + exit(1); +} diff --git a/tests/lib/libc/db/t_db.sh b/tests/lib/libc/db/t_db.sh new file mode 100644 index 000000000000..7d93d07452a6 --- /dev/null +++ b/tests/lib/libc/db/t_db.sh @@ -0,0 +1,903 @@ +# $NetBSD: t_db.sh,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ +# +# Copyright (c) 2008 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +prog() +{ + echo $(atf_get_srcdir)/h_db +} + +dict() +{ + if [ -f /usr/share/dict/words ]; then + echo /usr/share/dict/words + elif [ -f /usr/dict/words ]; then + echo /usr/dict/words + else + atf_fail "no dictionary found" + fi +} + +SEVEN_SEVEN="abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg|abcdefg" + +atf_test_case small_btree +small_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." +} +small_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + for i in `sed 200q $(dict)`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" btree in +} + +atf_test_case small_hash +small_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." +} +small_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + for i in `sed 200q $(dict)`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" hash in +} + +atf_test_case small_recno +small_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and small data" \ + "pairs: takes the first hundred entries in the dictionary," \ + "and makes them be key/data pairs." +} +small_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + sed 200q $(dict) >exp + + sed 200q $(dict) | + awk '{ + ++i; + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' >in + + atf_check -o file:exp "$(prog)" recno in +} + +atf_test_case medium_btree +medium_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." +} +medium_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + for i in $(sed 200q $(dict)); do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" btree in +} + +atf_test_case medium_hash +medium_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." +} +medium_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + for i in $(sed 200q $(dict)); do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" hash in +} + +atf_test_case medium_recno +medium_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and medium" \ + "data pairs: takes the first 200 entries in the" \ + "dictionary, and gives them each a medium size data entry." +} +medium_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) print $0 }' >exp + + echo $mdata | + awk '{ for (i = 1; i < 201; ++i) + printf("p\nk%d\nd%s\ng\nk%d\n", i, $0, i); + }' >in + + atf_check -o file:exp "$(prog)" recno in +} + +atf_test_case big_btree +big_btree_head() +{ + atf_set "descr" \ + "Checks btree database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + for psize in 512 16384 65536; do + echo "checking page size: $psize" + + for i in `find /bin -type f -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done >in + + atf_check "$(prog)" -o out btree in + cmp -s exp out || atf_fail "test failed for page size: $psize" + done +} + +atf_test_case big_hash +big_hash_head() +{ + atf_set "descr" \ + "Checks hash database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + for i in `find /bin -type f -print`; do + echo p + echo k$i + echo D$i + echo g + echo k$i + done >in + + atf_check "$(prog)" -o out hash in + cmp -s exp out || atf_fail "test failed" +} + +atf_test_case big_recno +big_recno_head() +{ + atf_set "descr" \ + "Checks recno database using small keys and big data" \ + "pairs: inserts the programs in /bin with their paths" \ + "as their keys." +} +big_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + (find /bin -type f -print | xargs cat) >exp + + find /bin -type f -print | + awk '{ + ++i; + printf("p\nk%d\nD%s\ng\nk%d\n", i, $0, i); + }' >in + + for psize in 512 16384 65536; do + echo "checking page size: $psize" + + atf_check "$(prog)" -o out recno in + cmp -s exp out || atf_fail "test failed for page size: $psize" + done +} + +atf_test_case random_recno +random_recno_head() +{ + atf_set "descr" "Checks recno database using random entries" +} +random_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 37; i <= 37 + 88 * 17; i += 17) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 1; i <= 15; ++i) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 19234; i <= 19234 + 61 * 27; i += 27) { + if (i % 41) + s = substr($0, 1, i % 41); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit + }' >exp + + cat exp | + awk 'BEGIN { + i = 37; + incr = 17; + } + { + printf("p\nk%d\nd%s\n", i, $0); + if (i == 19234 + 61 * 27) + exit; + if (i == 37 + 88 * 17) { + i = 1; + incr = 1; + } else if (i == 15) { + i = 19234; + incr = 27; + } else + i += incr; + } + END { + for (i = 37; i <= 37 + 88 * 17; i += 17) + printf("g\nk%d\n", i); + for (i = 1; i <= 15; ++i) + printf("g\nk%d\n", i); + for (i = 19234; i <= 19234 + 61 * 27; i += 27) + printf("g\nk%d\n", i); + }' >in + + atf_check -o file:exp "$(prog)" recno in +} + +atf_test_case reverse_recno +reverse_recno_head() +{ + atf_set "descr" "Checks recno database using reverse order entries" +} +reverse_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk ' { + for (i = 1500; i; --i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' >exp + + cat exp | + awk 'BEGIN { + i = 1500; + } + { + printf("p\nk%d\nd%s\n", i, $0); + --i; + } + END { + for (i = 1500; i; --i) + printf("g\nk%d\n", i); + }' >in + + atf_check -o file:exp "$(prog)" recno in +} + +atf_test_case alternate_recno +alternate_recno_head() +{ + atf_set "descr" "Checks recno database using alternating order entries" +} +alternate_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk ' { + for (i = 1; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + for (i = 2; i < 1200; i += 2) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("input key %d: %s\n", i, s); + } + exit; + }' >exp + + cat exp | + awk 'BEGIN { + i = 1; + even = 0; + } + { + printf("p\nk%d\nd%s\n", i, $0); + i += 2; + if (i >= 1200) { + if (even == 1) + exit; + even = 1; + i = 2; + } + } + END { + for (i = 1; i < 1200; ++i) + printf("g\nk%d\n", i); + }' >in + + atf_check "$(prog)" -o out recno in + + sort -o exp exp + sort -o out out + + cmp -s exp out || atf_fail "test failed" +} + +h_delete() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + type=$1 + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 120; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + printf("%05d: input key %d: %s\n", 120, 120, $0); + printf("seq failed, no such key\n"); + printf("%05d: input key %d: %s\n", 1, 1, $0); + printf("%05d: input key %d: %s\n", 2, 2, $0); + exit; + }' >exp + + cat exp | + awk '{ + if (i == 120) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_NEXT\n"); + for (i = 1; i <= 120; ++i) + printf("s\n"); + printf("fR_CURSOR\ns\nk120\n"); + printf("r\n"); + printf("fR_NEXT\ns\n"); + printf("fR_CURSOR\ns\nk1\n"); + printf("r\n"); + printf("fR_FIRST\ns\n"); + }' >in + + atf_check "$(prog)" -o out $type in + atf_check -o file:exp cat out +} + +# FIXME: should it actually work? the original test apparently +# was supposed to run such test, but didn't +atf_test_case delete_btree +delete_btree_head() +{ + atf_set "descr" "Checks removing records in btree database" +} +delete_btree_body() +{ +# +# The delete_btree test was skipped in the original ..../regress test +# structure, so noone ever noticed that it didn't work! Disable it for +# now, until we correct the generation of in/out files to reflect the +# actual collating sequence of key values ("19" comes before "2") +# + atf_skip "delete_btreee test case is broken" + h_delete btree +} + +atf_test_case delete_recno +delete_recno_head() +{ + atf_set "descr" "Checks removing records in recno database" +} +delete_recno_body() +{ + h_delete recno +} + +h_repeated() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo "" | + awk 'BEGIN { + for (i = 1; i <= 10; ++i) { + printf("p\nkkey1\nD/bin/sh\n"); + printf("p\nkkey2\nD/bin/csh\n"); + if (i % 8 == 0) { + printf("c\nkkey2\nD/bin/csh\n"); + printf("c\nkkey1\nD/bin/sh\n"); + printf("e\t%d of 10 (comparison)\n", i); + } else + printf("e\t%d of 10 \n", i); + printf("r\nkkey1\nr\nkkey2\n"); + } + }' >in + + $(prog) btree in +} + +atf_test_case repeated_btree +repeated_btree_head() +{ + atf_set "descr" \ + "Checks btree database with repeated small keys and" \ + "big data pairs. Makes sure that overflow pages are reused" +} +repeated_btree_body() +{ + h_repeated btree +} + +atf_test_case repeated_hash +repeated_hash_head() +{ + atf_set "descr" \ + "Checks hash database with repeated small keys and" \ + "big data pairs. Makes sure that overflow pages are reused" +} +repeated_hash_body() +{ + h_repeated hash +} + +atf_test_case duplicate_btree +duplicate_btree_head() +{ + atf_set "descr" "Checks btree database with duplicate keys" +} +duplicate_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 543; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + cat exp | + awk '{ + if (i++ % 2) + printf("p\nkduplicatekey\nd%s\n", $0); + else + printf("p\nkunique%dkey\nd%s\n", i, $0); + } + END { + printf("o\n"); + }' >in + + atf_check -o file:exp -x "$(prog) -iflags=1 btree in | sort" +} + +h_cursor_flags() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + type=$1 + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 20; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + # Test that R_CURSOR doesn't succeed before cursor initialized + cat exp | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\nr\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' >in + + atf_check -o ignore -e ignore -s ne:0 "$(prog)" -o out $type in + atf_check -s ne:0 test -s out + + cat exp | + awk '{ + if (i == 10) + exit; + printf("p\nk%d\nd%s\n", ++i, $0); + } + END { + printf("fR_CURSOR\np\nk1\ndsome data\n"); + printf("eR_CURSOR SHOULD HAVE FAILED\n"); + }' >in + + atf_check -o ignore -e ignore -s ne:0 "$(prog)" -o out $type in + atf_check -s ne:0 test -s out +} + +atf_test_case cursor_flags_btree +cursor_flags_btree_head() +{ + atf_set "descr" \ + "Checks use of cursor flags without initialization in btree database" +} +cursor_flags_btree_body() +{ + h_cursor_flags btree +} + +atf_test_case cursor_flags_recno +cursor_flags_recno_head() +{ + atf_set "descr" \ + "Checks use of cursor flags without initialization in recno database" +} +cursor_flags_recno_body() +{ + h_cursor_flags recno +} + +atf_test_case reverse_order_recno +reverse_order_recno_head() +{ + atf_set "descr" "Checks reverse order inserts in recno database" +} +reverse_order_recno_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 779; ++i) + printf("%05d: input key %d: %s\n", i, i, $0); + exit; + }' >exp + + cat exp | + awk '{ + if (i == 0) { + i = 1; + printf("p\nk1\nd%s\n", $0); + printf("%s\n", "fR_IBEFORE"); + } else + printf("p\nk1\nd%s\n", $0); + } + END { + printf("or\n"); + }' >in + + atf_check -o file:exp "$(prog)" recno in +} + +atf_test_case small_page_btree +small_page_btree_head() +{ + atf_set "descr" \ + "Checks btree database with lots of keys and small page" \ + "size: takes the first 20000 entries in the dictionary," \ + "reverses them, and gives them each a small size data" \ + "entry. Uses a small page size to make sure the btree" \ + "split code gets hammered." +} +small_page_btree_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + mdata=abcdefghijklmnopqrstuvwxy + echo $mdata | + awk '{ for (i = 1; i < 20001; ++i) print $0 }' >exp + + for i in `sed 20000q $(dict) | rev`; do + echo p + echo k$i + echo d$mdata + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" -i psize=512 btree in +} + +h_byte_orders() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + type=$1 + + sed 50q $(dict) >exp + for order in 1234 4321; do + for i in `sed 50q $(dict)`; do + echo p + echo k$i + echo d$i + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" -ilorder=$order -f byte.file $type in + + for i in `sed 50q $(dict)`; do + echo g + echo k$i + done >in + + atf_check -o file:exp "$(prog)" -s -ilorder=$order -f byte.file $type in + done +} + +atf_test_case byte_orders_btree +byte_orders_btree_head() +{ + atf_set "descr" "Checks btree database using differing byte orders" +} +byte_orders_btree_body() +{ + h_byte_orders btree +} + +atf_test_case byte_orders_hash +byte_orders_hash_head() +{ + atf_set "descr" "Checks hash database using differing byte orders" +} +byte_orders_hash_body() +{ + h_byte_orders hash +} + +h_bsize_ffactor() +{ + bsize=$1 + ffactor=$2 + + echo "bucketsize $bsize, fill factor $ffactor" + atf_check -o file:exp "$(prog)" "-ibsize=$bsize,\ +ffactor=$ffactor,nelem=25000,cachesize=65536" hash in +} + +atf_test_case bsize_ffactor +bsize_ffactor_head() +{ + atf_set "descr" "Checks hash database with various" \ + "bucketsizes and fill factors" +} +bsize_ffactor_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + echo $SEVEN_SEVEN | + awk '{ + for (i = 1; i <= 10000; ++i) { + if (i % 34) + s = substr($0, 1, i % 34); + else + s = substr($0, 1); + printf("%s\n", s); + } + exit; + + }' >exp + + sed 10000q $(dict) | + awk 'BEGIN { + ds="'$SEVEN_SEVEN'" + } + { + if (++i % 34) + s = substr(ds, 1, i % 34); + else + s = substr(ds, 1); + printf("p\nk%s\nd%s\n", $0, s); + }' >in + + sed 10000q $(dict) | + awk '{ + ++i; + printf("g\nk%s\n", $0); + }' >>in + + h_bsize_ffactor 256 11 + h_bsize_ffactor 256 14 + h_bsize_ffactor 256 21 + + h_bsize_ffactor 512 21 + h_bsize_ffactor 512 28 + h_bsize_ffactor 512 43 + + h_bsize_ffactor 1024 43 + h_bsize_ffactor 1024 57 + h_bsize_ffactor 1024 85 + + h_bsize_ffactor 2048 85 + h_bsize_ffactor 2048 114 + h_bsize_ffactor 2048 171 + + h_bsize_ffactor 4096 171 + h_bsize_ffactor 4096 228 + h_bsize_ffactor 4096 341 + + h_bsize_ffactor 8192 341 + h_bsize_ffactor 8192 455 + h_bsize_ffactor 8192 683 +} + +# FIXME: what does it test? +atf_test_case four_char_hash +four_char_hash_head() +{ + atf_set "descr" \ + "Checks hash database with 4 char key and" \ + "value insert on a 65536 bucket size" +} +four_char_hash_body() +{ + TMPDIR="$(pwd)/db_dir"; export TMPDIR + mkdir ${TMPDIR} + + cat >in <