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