xref: /freebsd/lib/libc/db/test/btree.tests/main.c (revision dc36d6f9)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Mike Olson.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <fcntl.h>
37 #include <db.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include "btree.h"
44 
45 typedef struct cmd_table {
46 	char *cmd;
47 	int nargs;
48 	int rconv;
49 	void (*func)(DB *, char **);
50 	char *usage, *descrip;
51 } cmd_table;
52 
53 int stopstop;
54 DB *globaldb;
55 
56 void append(DB *, char **);
57 void bstat(DB *, char **);
58 void cursor(DB *, char **);
59 void delcur(DB *, char **);
60 void delete(DB *, char **);
61 void dump(DB *, char **);
62 void first(DB *, char **);
63 void get(DB *, char **);
64 void help(DB *, char **);
65 void iafter(DB *, char **);
66 void ibefore(DB *, char **);
67 void icursor(DB *, char **);
68 void insert(DB *, char **);
69 void keydata(DBT *, DBT *);
70 void last(DB *, char **);
71 void list(DB *, char **);
72 void load(DB *, char **);
73 void mstat(DB *, char **);
74 void next(DB *, char **);
75 int  parse(char *, char **, int);
76 void previous(DB *, char **);
77 void show(DB *, char **);
78 void usage(void);
79 void user(DB *);
80 
81 cmd_table commands[] = {
82 	"?",	0, 0, help, "help", NULL,
83 	"a",	2, 1, append, "append key def", "append key with data def",
84 	"b",	0, 0, bstat, "bstat", "stat btree",
85 	"c",	1, 1, cursor,  "cursor word", "move cursor to word",
86 	"delc",	0, 0, delcur, "delcur", "delete key the cursor references",
87 	"dele",	1, 1, delete, "delete word", "delete word",
88 	"d",	0, 0, dump, "dump", "dump database",
89 	"f",	0, 0, first, "first", "move cursor to first record",
90 	"g",	1, 1, get, "get key", "locate key",
91 	"h",	0, 0, help, "help", "print command summary",
92 	"ia",	2, 1, iafter, "iafter key data", "insert data after key",
93 	"ib",	2, 1, ibefore, "ibefore key data", "insert data before key",
94 	"ic",	2, 1, icursor, "icursor key data", "replace cursor",
95 	"in",	2, 1, insert, "insert key def", "insert key with data def",
96 	"la",	0, 0, last, "last", "move cursor to last record",
97 	"li",	1, 1, list, "list file", "list to a file",
98 	"loa",	1, 0, load, "load file", NULL,
99 	"loc",	1, 1, get, "get key", NULL,
100 	"m",	0, 0, mstat, "mstat", "stat memory pool",
101 	"n",	0, 0, next, "next", "move cursor forward one record",
102 	"p",	0, 0, previous, "previous", "move cursor back one record",
103 	"q",	0, 0, NULL, "quit", "quit",
104 	"sh",	1, 0, show, "show page", "dump a page",
105 	{ NULL },
106 };
107 
108 int recno;					/* use record numbers */
109 char *dict = "words";				/* default dictionary */
110 char *progname;
111 
112 int
main(argc,argv)113 main(argc, argv)
114 	int argc;
115 	char **argv;
116 {
117 	int c;
118 	DB *db;
119 	BTREEINFO b;
120 
121 	progname = *argv;
122 
123 	b.flags = 0;
124 	b.cachesize = 0;
125 	b.maxkeypage = 0;
126 	b.minkeypage = 0;
127 	b.psize = 0;
128 	b.compare = NULL;
129 	b.prefix = NULL;
130 	b.lorder = 0;
131 
132 	while ((c = getopt(argc, argv, "bc:di:lp:ru")) != -1) {
133 		switch (c) {
134 		case 'b':
135 			b.lorder = BIG_ENDIAN;
136 			break;
137 		case 'c':
138 			b.cachesize = atoi(optarg);
139 			break;
140 		case 'd':
141 			b.flags |= R_DUP;
142 			break;
143 		case 'i':
144 			dict = optarg;
145 			break;
146 		case 'l':
147 			b.lorder = LITTLE_ENDIAN;
148 			break;
149 		case 'p':
150 			b.psize = atoi(optarg);
151 			break;
152 		case 'r':
153 			recno = 1;
154 			break;
155 		case 'u':
156 			b.flags = 0;
157 			break;
158 		default:
159 			usage();
160 		}
161 	}
162 	argc -= optind;
163 	argv += optind;
164 
165 	if (recno)
166 		db = dbopen(*argv == NULL ? NULL : *argv, O_RDWR,
167 		    0, DB_RECNO, NULL);
168 	else
169 		db = dbopen(*argv == NULL ? NULL : *argv, O_CREAT|O_RDWR,
170 		    0600, DB_BTREE, &b);
171 
172 	if (db == NULL) {
173 		(void)fprintf(stderr, "dbopen: %s\n", strerror(errno));
174 		exit(1);
175 	}
176 	globaldb = db;
177 	user(db);
178 	exit(0);
179 	/* NOTREACHED */
180 }
181 
182 void
user(db)183 user(db)
184 	DB *db;
185 {
186 	FILE *ifp;
187 	int argc, i, last;
188 	char *lbuf, *argv[4], buf[512];
189 
190 	if ((ifp = fopen("/dev/tty", "r")) == NULL) {
191 		(void)fprintf(stderr,
192 		    "/dev/tty: %s\n", strerror(errno));
193 		exit(1);
194 	}
195 	for (last = 0;;) {
196 		(void)printf("> ");
197 		(void)fflush(stdout);
198 		if ((lbuf = fgets(&buf[0], 512, ifp)) == NULL)
199 			break;
200 		if (lbuf[0] == '\n') {
201 			i = last;
202 			goto uselast;
203 		}
204 		lbuf[strlen(lbuf) - 1] = '\0';
205 
206 		if (lbuf[0] == 'q')
207 			break;
208 
209 		argc = parse(lbuf, &argv[0], 3);
210 		if (argc == 0)
211 			continue;
212 
213 		for (i = 0; commands[i].cmd != NULL; i++)
214 			if (strncmp(commands[i].cmd, argv[0],
215 			    strlen(commands[i].cmd)) == 0)
216 				break;
217 
218 		if (commands[i].cmd == NULL) {
219 			(void)fprintf(stderr,
220 			    "%s: command unknown ('help' for help)\n", lbuf);
221 			continue;
222 		}
223 
224 		if (commands[i].nargs != argc - 1) {
225 			(void)fprintf(stderr, "usage: %s\n", commands[i].usage);
226 			continue;
227 		}
228 
229 		if (recno && commands[i].rconv) {
230 			static recno_t nlong;
231 			nlong = atoi(argv[1]);
232 			argv[1] = (char *)&nlong;
233 		}
234 uselast:	last = i;
235 		(*commands[i].func)(db, argv);
236 	}
237 	if ((db->sync)(db) == RET_ERROR)
238 		perror("dbsync");
239 	else if ((db->close)(db) == RET_ERROR)
240 		perror("dbclose");
241 }
242 
243 int
parse(lbuf,argv,maxargc)244 parse(lbuf, argv, maxargc)
245 	char *lbuf, **argv;
246 	int maxargc;
247 {
248 	int argc = 0;
249 	char *c;
250 
251 	c = lbuf;
252 	while (isspace(*c))
253 		c++;
254 	while (*c != '\0' && argc < maxargc) {
255 		*argv++ = c;
256 		argc++;
257 		while (!isspace(*c) && *c != '\0') {
258 			c++;
259 		}
260 		while (isspace(*c))
261 			*c++ = '\0';
262 	}
263 	return (argc);
264 }
265 
266 void
append(db,argv)267 append(db, argv)
268 	DB *db;
269 	char **argv;
270 {
271 	DBT key, data;
272 	int status;
273 
274 	if (!recno) {
275 		(void)fprintf(stderr,
276 		    "append only available for recno db's.\n");
277 		return;
278 	}
279 	key.data = argv[1];
280 	key.size = sizeof(recno_t);
281 	data.data = argv[2];
282 	data.size = strlen(data.data);
283 	status = (db->put)(db, &key, &data, R_APPEND);
284 	switch (status) {
285 	case RET_ERROR:
286 		perror("append/put");
287 		break;
288 	case RET_SPECIAL:
289 		(void)printf("%s (duplicate key)\n", argv[1]);
290 		break;
291 	case RET_SUCCESS:
292 		break;
293 	}
294 }
295 
296 void
cursor(db,argv)297 cursor(db, argv)
298 	DB *db;
299 	char **argv;
300 {
301 	DBT data, key;
302 	int status;
303 
304 	key.data = argv[1];
305 	if (recno)
306 		key.size = sizeof(recno_t);
307 	else
308 		key.size = strlen(argv[1]) + 1;
309 	status = (*db->seq)(db, &key, &data, R_CURSOR);
310 	switch (status) {
311 	case RET_ERROR:
312 		perror("cursor/seq");
313 		break;
314 	case RET_SPECIAL:
315 		(void)printf("key not found\n");
316 		break;
317 	case RET_SUCCESS:
318 		keydata(&key, &data);
319 		break;
320 	}
321 }
322 
323 void
delcur(db,argv)324 delcur(db, argv)
325 	DB *db;
326 	char **argv;
327 {
328 	int status;
329 
330 	status = (*db->del)(db, NULL, R_CURSOR);
331 
332 	if (status == RET_ERROR)
333 		perror("delcur/del");
334 }
335 
336 void
delete(db,argv)337 delete(db, argv)
338 	DB *db;
339 	char **argv;
340 {
341 	DBT key;
342 	int status;
343 
344 	key.data = argv[1];
345 	if (recno)
346 		key.size = sizeof(recno_t);
347 	else
348 		key.size = strlen(argv[1]) + 1;
349 
350 	status = (*db->del)(db, &key, 0);
351 	switch (status) {
352 	case RET_ERROR:
353 		perror("delete/del");
354 		break;
355 	case RET_SPECIAL:
356 		(void)printf("key not found\n");
357 		break;
358 	case RET_SUCCESS:
359 		break;
360 	}
361 }
362 
363 void
dump(db,argv)364 dump(db, argv)
365 	DB *db;
366 	char **argv;
367 {
368 	__bt_dump(db);
369 }
370 
371 void
first(db,argv)372 first(db, argv)
373 	DB *db;
374 	char **argv;
375 {
376 	DBT data, key;
377 	int status;
378 
379 	status = (*db->seq)(db, &key, &data, R_FIRST);
380 
381 	switch (status) {
382 	case RET_ERROR:
383 		perror("first/seq");
384 		break;
385 	case RET_SPECIAL:
386 		(void)printf("no more keys\n");
387 		break;
388 	case RET_SUCCESS:
389 		keydata(&key, &data);
390 		break;
391 	}
392 }
393 
394 void
get(db,argv)395 get(db, argv)
396 	DB *db;
397 	char **argv;
398 {
399 	DBT data, key;
400 	int status;
401 
402 	key.data = argv[1];
403 	if (recno)
404 		key.size = sizeof(recno_t);
405 	else
406 		key.size = strlen(argv[1]) + 1;
407 
408 	status = (*db->get)(db, &key, &data, 0);
409 
410 	switch (status) {
411 	case RET_ERROR:
412 		perror("get/get");
413 		break;
414 	case RET_SPECIAL:
415 		(void)printf("key not found\n");
416 		break;
417 	case RET_SUCCESS:
418 		keydata(&key, &data);
419 		break;
420 	}
421 }
422 
423 void
help(db,argv)424 help(db, argv)
425 	DB *db;
426 	char **argv;
427 {
428 	int i;
429 
430 	for (i = 0; commands[i].cmd; i++)
431 		if (commands[i].descrip)
432 			(void)printf("%s: %s\n",
433 			    commands[i].usage, commands[i].descrip);
434 }
435 
436 void
iafter(db,argv)437 iafter(db, argv)
438 	DB *db;
439 	char **argv;
440 {
441 	DBT key, data;
442 	int status;
443 
444 	if (!recno) {
445 		(void)fprintf(stderr,
446 		    "iafter only available for recno db's.\n");
447 		return;
448 	}
449 	key.data = argv[1];
450 	key.size = sizeof(recno_t);
451 	data.data = argv[2];
452 	data.size = strlen(data.data);
453 	status = (db->put)(db, &key, &data, R_IAFTER);
454 	switch (status) {
455 	case RET_ERROR:
456 		perror("iafter/put");
457 		break;
458 	case RET_SPECIAL:
459 		(void)printf("%s (duplicate key)\n", argv[1]);
460 		break;
461 	case RET_SUCCESS:
462 		break;
463 	}
464 }
465 
466 void
ibefore(db,argv)467 ibefore(db, argv)
468 	DB *db;
469 	char **argv;
470 {
471 	DBT key, data;
472 	int status;
473 
474 	if (!recno) {
475 		(void)fprintf(stderr,
476 		    "ibefore only available for recno db's.\n");
477 		return;
478 	}
479 	key.data = argv[1];
480 	key.size = sizeof(recno_t);
481 	data.data = argv[2];
482 	data.size = strlen(data.data);
483 	status = (db->put)(db, &key, &data, R_IBEFORE);
484 	switch (status) {
485 	case RET_ERROR:
486 		perror("ibefore/put");
487 		break;
488 	case RET_SPECIAL:
489 		(void)printf("%s (duplicate key)\n", argv[1]);
490 		break;
491 	case RET_SUCCESS:
492 		break;
493 	}
494 }
495 
496 void
icursor(db,argv)497 icursor(db, argv)
498 	DB *db;
499 	char **argv;
500 {
501 	int status;
502 	DBT data, key;
503 
504 	key.data = argv[1];
505 	if (recno)
506 		key.size = sizeof(recno_t);
507 	else
508 		key.size = strlen(argv[1]) + 1;
509 	data.data = argv[2];
510 	data.size = strlen(argv[2]) + 1;
511 
512 	status = (*db->put)(db, &key, &data, R_CURSOR);
513 	switch (status) {
514 	case RET_ERROR:
515 		perror("icursor/put");
516 		break;
517 	case RET_SPECIAL:
518 		(void)printf("%s (duplicate key)\n", argv[1]);
519 		break;
520 	case RET_SUCCESS:
521 		break;
522 	}
523 }
524 
525 void
insert(db,argv)526 insert(db, argv)
527 	DB *db;
528 	char **argv;
529 {
530 	int status;
531 	DBT data, key;
532 
533 	key.data = argv[1];
534 	if (recno)
535 		key.size = sizeof(recno_t);
536 	else
537 		key.size = strlen(argv[1]) + 1;
538 	data.data = argv[2];
539 	data.size = strlen(argv[2]) + 1;
540 
541 	status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
542 	switch (status) {
543 	case RET_ERROR:
544 		perror("insert/put");
545 		break;
546 	case RET_SPECIAL:
547 		(void)printf("%s (duplicate key)\n", argv[1]);
548 		break;
549 	case RET_SUCCESS:
550 		break;
551 	}
552 }
553 
554 void
last(db,argv)555 last(db, argv)
556 	DB *db;
557 	char **argv;
558 {
559 	DBT data, key;
560 	int status;
561 
562 	status = (*db->seq)(db, &key, &data, R_LAST);
563 
564 	switch (status) {
565 	case RET_ERROR:
566 		perror("last/seq");
567 		break;
568 	case RET_SPECIAL:
569 		(void)printf("no more keys\n");
570 		break;
571 	case RET_SUCCESS:
572 		keydata(&key, &data);
573 		break;
574 	}
575 }
576 
577 void
list(db,argv)578 list(db, argv)
579 	DB *db;
580 	char **argv;
581 {
582 	DBT data, key;
583 	FILE *fp;
584 	int status;
585 
586 	if ((fp = fopen(argv[1], "w")) == NULL) {
587 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
588 		return;
589 	}
590 	status = (*db->seq)(db, &key, &data, R_FIRST);
591 	while (status == RET_SUCCESS) {
592 		(void)fprintf(fp, "%s\n", key.data);
593 		status = (*db->seq)(db, &key, &data, R_NEXT);
594 	}
595 	if (status == RET_ERROR)
596 		perror("list/seq");
597 }
598 
599 DB *BUGdb;
600 void
load(db,argv)601 load(db, argv)
602 	DB *db;
603 	char **argv;
604 {
605 	char *p, *t;
606 	FILE *fp;
607 	DBT data, key;
608 	recno_t cnt;
609 	size_t len;
610 	int status;
611 	char *lp, buf[16 * 1024];
612 
613 	BUGdb = db;
614 	if ((fp = fopen(argv[1], "r")) == NULL) {
615 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
616 		return;
617 	}
618 	(void)printf("loading %s...\n", argv[1]);
619 
620 	for (cnt = 1; (lp = fgetline(fp, &len)) != NULL; ++cnt) {
621 		if (recno) {
622 			key.data = &cnt;
623 			key.size = sizeof(recno_t);
624 			data.data = lp;
625 			data.size = len + 1;
626 		} else {
627 			key.data = lp;
628 			key.size = len + 1;
629 			for (p = lp + len - 1, t = buf; p >= lp; *t++ = *p--);
630 			*t = '\0';
631 			data.data = buf;
632 			data.size = len + 1;
633 		}
634 
635 		status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
636 		switch (status) {
637 		case RET_ERROR:
638 			perror("load/put");
639 			exit(1);
640 		case RET_SPECIAL:
641 			if (recno)
642 				(void)fprintf(stderr,
643 				    "duplicate: %ld {%s}\n", cnt, data.data);
644 			else
645 				(void)fprintf(stderr,
646 				    "duplicate: %ld {%s}\n", cnt, key.data);
647 			exit(1);
648 		case RET_SUCCESS:
649 			break;
650 		}
651 	}
652 	(void)fclose(fp);
653 }
654 
655 void
next(db,argv)656 next(db, argv)
657 	DB *db;
658 	char **argv;
659 {
660 	DBT data, key;
661 	int status;
662 
663 	status = (*db->seq)(db, &key, &data, R_NEXT);
664 
665 	switch (status) {
666 	case RET_ERROR:
667 		perror("next/seq");
668 		break;
669 	case RET_SPECIAL:
670 		(void)printf("no more keys\n");
671 		break;
672 	case RET_SUCCESS:
673 		keydata(&key, &data);
674 		break;
675 	}
676 }
677 
678 void
previous(db,argv)679 previous(db, argv)
680 	DB *db;
681 	char **argv;
682 {
683 	DBT data, key;
684 	int status;
685 
686 	status = (*db->seq)(db, &key, &data, R_PREV);
687 
688 	switch (status) {
689 	case RET_ERROR:
690 		perror("previous/seq");
691 		break;
692 	case RET_SPECIAL:
693 		(void)printf("no more keys\n");
694 		break;
695 	case RET_SUCCESS:
696 		keydata(&key, &data);
697 		break;
698 	}
699 }
700 
701 void
show(db,argv)702 show(db, argv)
703 	DB *db;
704 	char **argv;
705 {
706 	BTREE *t;
707 	PAGE *h;
708 	pgno_t pg;
709 
710 	pg = atoi(argv[1]);
711 	t = db->internal;
712 	if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) {
713 		(void)printf("getpage of %ld failed\n", pg);
714 		return;
715 	}
716 	if (pg == 0)
717 		__bt_dmpage(h);
718 	else
719 		__bt_dpage(h);
720 	mpool_put(t->bt_mp, h, 0);
721 }
722 
723 void
bstat(db,argv)724 bstat(db, argv)
725 	DB *db;
726 	char **argv;
727 {
728 	(void)printf("BTREE\n");
729 	__bt_stat(db);
730 }
731 
732 void
mstat(db,argv)733 mstat(db, argv)
734 	DB *db;
735 	char **argv;
736 {
737 	(void)printf("MPOOL\n");
738 	mpool_stat(((BTREE *)db->internal)->bt_mp);
739 }
740 
741 void
keydata(key,data)742 keydata(key, data)
743 	DBT *key, *data;
744 {
745 	if (!recno && key->size > 0)
746 		(void)printf("%s/", key->data);
747 	if (data->size > 0)
748 		(void)printf("%s", data->data);
749 	(void)printf("\n");
750 }
751 
752 void
usage()753 usage()
754 {
755 	(void)fprintf(stderr,
756 	    "usage: %s [-bdlu] [-c cache] [-i file] [-p page] [file]\n",
757 	    progname);
758 	exit (1);
759 }
760