1 /*
2  * Copyright (c) 2011, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file EXAMPLES-LICENSE for license information.
5  *
6  * $Id$
7  *
8  * ex_heap -- A basic example about usage of heap access method.
9  *
10  *   This program demonstrates the usage of heap access method of BDB and differences
11  *   between the heap and btree access method. There are many kinds of access methods
12  *   for BDB, access methods determine how pages are stored in the file and how records
13  *   are stored on a page. Heap access method is one of the common used access method
14  *   for BDB. The records are unsorted and the key indicates the page location within
15  *   heap access method. It's feature is to reuse space easily and enable the database
16  *   to stay a constant size, which is different from btree access method.
17  *
18  *   The application initially populates a database, and then proceeds to move into
19  *   a process of adding and removing data. The sample application using a btree
20  *   database will be run if the flag is specified. The system will keep a fairly
21  *   constant amount of data in the database. After the insert/delete operation
22  *   is completed. The system will calculate the execution time and physical file
23  *   size to demonstrate the difference between btree database and heap database.
24  *   The outcome shows that heap access method will maintain a constant database
25  *   size if the heap size is configured properly, while the btree database will
26  *   continue to grow.
27  *
28  * Database: heap.db, btree.db
29  * Program name: ex_heap
30  *
31  * Options:
32  *   -b		run sample application using a btree database
33  *   -c		specify the cache size for the environment
34  *   -d		test on variable-length data (default:fix-length)
35  *   -h [dir]	specify the home directory for the environment
36  *   -n		specify the number of records per repetition (default:10000)
37  *   -p		specify the pgsize of database
38  *   -r		set number of repetition (a pair of insertion and deletion, default:1)
39  *   -s		specify the heap size (bytes) for the heap database
40  *   -S		specify the heap size (gbytes) for the heap database
41  *
42  */
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "db.h"
49 
50 
51 #ifndef lint
52 static const char copyright[] =
53     "Copyright (c) 2011, 2020 Oracle and/or its affiliates.  All rights reserved.\n";
54 #endif
55 
56 #define	BUFFER_LEN		30     /* Buffer size to hold data */
57 #define	NS_PER_MS		1000000/* Nanoseconds in a millisecond*/
58 #define	NS_PER_US		1000   /* Nanoseconds in a microsecond*/
59 #define	DEF_INIT_RECS		10000  /* Default initial records */
60 #define	DEF_RECS_PER_REP	10000  /* Default records per repeat. */
61 #define	DEF_REPEATS		1      /* Default repetition value */
62 
63 /*
64  * Average space each record needs, based on the data generated.
65  * The ideal heap size for this example should be set as (bytes):
66  * AVG_SPACE_PER_RECORD * (DEF_INIT_RECS + records inserted each repetition)
67  */
68 #define	AVG_SPACE_PER_RECORD	36
69 
70 #ifdef _WIN32
71 #include <sys/timeb.h>
72 #include <time.h>
73 #include <winsock2.h>
74 extern int getopt(int, char * const *, const char *);
75 
76 /* Implement a basic high-resolution timer with a POSIX interface for Windows.*/
gettimeofday(struct timeval * tv,struct timezone * tz)77 int gettimeofday(struct timeval *tv, struct timezone *tz)
78 {
79 	struct _timeb now;
80 	_ftime(&now);
81 	tv->tv_sec = (long)now.time;
82 	tv->tv_usec = now.millitm * NS_PER_US;
83 	return (0);
84 }
85 #else
86 #include <sys/time.h>
87 #include <unistd.h>
88 #endif
89 
90 int	compare_int(DB *, const DBT *, const DBT *, size_t *);
91 int	delete_recs __P((DB *, DB_ENV *, int));
92 int	file_size __P((DB *, DBTYPE, int *));
93 int	generate_data __P((char [], int, int));
94 int	insert_btree __P((DB *, DB_ENV *, int, int, int));
95 int	insert_heap __P((DB *, DB_ENV *, int, int, int));
96 int	open_db __P((
97     DB **, DB_ENV *, DBTYPE, char *, u_int32_t, u_int32_t, u_int32_t));
98 int	open_env __P((DB_ENV **, char *, u_int32_t));
99 int	run_workload __P((DB *, int, int, int));
100 void	usage __P((void));
101 
102 const char *progname = "ex_heap"; /* Program name. */
103 
104 int
main(argc,argv)105 main(argc, argv)
106 	int argc;
107 	char *argv[];
108 {
109 	extern char *optarg;	/* Set by getopt(): the argv being processed. */
110 	DB_ENV *dbenv;		/* The environment handle. */
111 	DB *dbp;		/* The database handle. */
112 	u_int32_t cachesize;	/* The size of cache set for the environment. */
113 	u_int32_t ghpsize;	/* The heap size (gbytes) for the heap database. */
114 	u_int32_t hpsize;	/* The heap size (bytes) for the heap database. */
115 	u_int32_t pgsize;	/* The page size of database. */
116 	char *home;		/* The home directory for the program. */
117 	int ch;			/* The current command line option char. */
118 	int ret;		/* Return code from call into Berkeley DB. */
119 	int ret_t;		/* Return code from close operation. */
120 	int set_ghpsize;	/* Flag for setting gphsize. */
121 	int set_hpsize;		/* Flag for setting hpsize. */
122 	int test_btree;		/* Flag for running sample application using a btree database. */
123 	int test_var_data;	/* Flag for testing variable-length data. */
124 
125 	int recs_per_rep;	/* Number of records per repetition. */
126 	int repeats;		/* Repetition value. */
127 
128 	dbenv = NULL;
129 	dbp = NULL;
130 	cachesize = 0;
131 	ret = ret_t = set_ghpsize = set_hpsize = test_btree = 0;
132 	home = NULL;
133 
134 	recs_per_rep = DEF_RECS_PER_REP;
135 	ghpsize = hpsize = pgsize = 0;
136 	repeats = DEF_REPEATS;
137 	test_var_data = 0; /* Default as fix-length data. */
138 
139 	/* Parse the command line arguments */
140 	while ((ch = getopt(argc, argv, "bc:dh:n:p:r:S:s:")) != EOF)
141 		switch (ch) {
142 		case 'b':		/* Run sample application using a btree database. */
143 			test_btree = 1;
144 			break;
145 		case 'c':		/* Specify the cache size for the environment. */
146 			cachesize = atoi(optarg);
147 			break;
148 		case 'd':		/* Test on variable-length data. */
149 			test_var_data = 1;
150 			break;
151 		case 'h':		/* Specify the home directory for the environment. */
152 			home = optarg;
153 			break;
154 		case 'n':		/* Specify the number of records per repetition. */
155 			recs_per_rep = atoi(optarg);
156 			break;
157 		case 'p':		/* Specify the pgsize of database. */
158 			pgsize = atoi(optarg);
159 			break;
160 		case 'r':		/* Set number of repetition. */
161 			repeats = atoi(optarg);
162 			break;
163 		case 's':		/* Specify the heap size (bytes) for the heap database. */
164 			set_hpsize = 1;
165 			hpsize = atoi(optarg);
166 			break;
167 		case 'S':		/* Specify the heap size (gbytes) for the heap database. */
168 			set_ghpsize = 1;
169 			ghpsize = atoi(optarg);
170 			break;
171 		default:
172 			usage();
173 		}
174 
175 	if (!home)
176 		usage();
177 
178 	srand((int)time(NULL));
179 
180 	/*
181 	 * If heap size is not specified, then use our default configuration
182 	 * as follows.
183 	 */
184 	if (!set_hpsize && !set_ghpsize)
185 		hpsize = AVG_SPACE_PER_RECORD * (DEF_INIT_RECS + recs_per_rep);
186 
187 	/* Open the evnironment for usage. */
188 	if ((ret = open_env(&dbenv, home, cachesize)) != 0) {
189 		fprintf(stderr, "%s: open_env: %s", progname, db_strerror(ret));
190 		goto err;
191 	}
192 	/* Open a heap database for insert/delete operations. */
193 	if ((ret = open_db(&dbp, dbenv, DB_HEAP, home,
194 	    ghpsize, hpsize, pgsize)) != 0) {
195 		dbenv->err(dbenv, ret, "Failed to open heap database.");
196 		goto err;
197 	}
198 
199 	/*
200 	 * Perform requested rounds of insert/delete operations
201 	 * using heap database.
202 	 */
203 	if ((ret =
204 	    run_workload(dbp, repeats, recs_per_rep, test_var_data)) != 0) {
205 		dbenv->err(dbenv, ret,
206 		    "Failed to perform operations on heap database.");
207 		goto err;
208 	}
209 
210 	if (test_btree) {
211 		/* Close the DB handle for heap. */
212 		if ((ret = dbp->close(dbp, 0)) != 0) {
213 			dbenv->err(dbenv, ret, "DB->close");
214 			goto err;
215 		}
216 		dbp = NULL;
217 		/* Open a b-tree database for insert/delete operations. */
218 		if ((ret =
219 		    open_db(&dbp, dbenv, DB_BTREE, home, 0, 0, pgsize)) != 0) {
220 			dbenv->err(dbenv, ret,
221 			    "Failed to open btree database.");
222 			goto err;
223 		}
224 
225 		/*
226 		 * Perform requested rounds of insert/delete operations
227 		 * using btree database.
228 		 */
229 		if ((ret = run_workload(dbp,
230 		    repeats, recs_per_rep, test_var_data)) != 0) {
231 			dbenv->err(dbenv, ret,
232 			    "Failed to perform operations on btree database.");
233 			goto err;
234 		}
235 	}
236 err:
237 	if (dbp != NULL && (ret_t = dbp->close(dbp, 0)) != 0) {
238 		dbenv->err(dbenv, ret_t, "DB->close");
239 		ret = (ret == 0 ? ret_t : ret);
240 	}
241 
242 	if (dbenv != NULL && (ret_t = dbenv->close(dbenv, 0)) != 0) {
243 		fprintf(stderr, "%s: dbenv->close: %s", progname,
244 		    db_strerror(ret_t));
245 		ret = (ret == 0 ? ret_t : ret);
246 	}
247 
248 	return (ret);
249 }
250 
251 /*
252  * run_workload --
253  * 	Perform requested rounds of insert/delete operations.
254  *
255  * Parameters:
256  *	dbp		the database handle
257  *	repeats		the number of repetition (a pair of insertion and deletion)
258  *	recs_per_rep	the number of records per repetition
259  *	test_var	test on variable-length data flag
260  */
261 int
run_workload(dbp,repeats,recs_per_rep,test_var)262 run_workload(dbp, repeats, recs_per_rep, test_var)
263 	DB *dbp;
264 	int repeats, recs_per_rep, test_var;
265 {
266 	DB_ENV *dbenv;			/* The environment handle. */
267 	DBTYPE dbtype;			/* The access method used by the database. */
268 	u_int32_t ghpsize;		/* The heap size (gbytes). */
269 	u_int32_t hpsize;		/* The heap size (bytes). */
270 	struct timeval end_time;	/* The end time of each iteration. */
271 	struct timeval start_time;	/* The start time of each iteration. */
272 	double *time_secs;		/* The array of each iteration's run time. */
273 	int *db_file_sizes;		/* The array of file sizes after each iteration. */
274 	int fsize;			/* The file size after the current iteration. */
275 	int i;				/* Iteration number. */
276 	int ret;			/* The return value. */
277 
278 	dbenv = dbp->dbenv;
279 	fsize = 0;
280 	time_secs = NULL;
281 	db_file_sizes = NULL;
282 
283 	/* Get the access method of the database. */
284 	if ((ret = dbp->get_type(dbp, &dbtype)) != 0) {
285 		dbenv->err(dbenv, ret, "DB->get_type");
286 		goto err;
287 	}
288 
289 	/* Get the heap size if the database uses the heap access method. */
290 	if (dbtype == DB_HEAP &&
291 	    (ret = dbp->get_heapsize(dbp, &ghpsize, &hpsize)) != 0) {
292 		dbenv->err(dbenv, ret, "DB->get_heapsize");
293 		goto err;
294 	}
295 
296 	/* Initialize the array holding file sizes after each iteration. */
297 	if ((db_file_sizes =
298 	    (int *)malloc((repeats + 1) * sizeof(int))) == NULL) {
299 		fprintf(stderr,
300 		    "%s: Unable to allocate space for array db_file_sizes.\n",
301 		    progname);
302 		goto err;
303 	}
304 	memset(db_file_sizes, 0, (repeats + 1) * sizeof(int));
305 
306 	/* Initialize the array holding each iteration's run time. */
307 	if ((time_secs =
308 	    (double *)malloc((repeats + 1) * sizeof(double))) == NULL) {
309 		fprintf(stderr,
310 		    "%s: Unable to allocate space for array time_secs.\n",
311 		    progname);
312 		goto err;
313 	}
314 	memset(time_secs, 0, (repeats + 1) * sizeof(double));
315 
316 	printf("\n\n======================================================");
317 	printf("\nAbout to enter the insert phase.");
318 	printf("\n\tDatabase type: %s \t",
319 	    dbtype == DB_HEAP ? "Heap" : "Btree");
320 	if (dbtype == DB_HEAP)
321 		printf("with configured heapsize = %d gbytes and %d bytes.",
322 		    ghpsize, hpsize);
323 	printf("\n\tPagesize: %d", dbp->pgsize);
324 	printf("\n\tInitial records number: %d", DEF_INIT_RECS);
325 	printf("\n\tNumber of repetitions: %d", repeats);
326 	printf("\n\tNumber of inserts per repetition: %d\n", recs_per_rep);
327 
328 	/*
329 	 * Insert records to the database and delete the same number from
330 	 * the database, then check the change of the physical database file.
331 	 *
332 	 * Don't delete after the first insertion to leave some data
333 	 * in the tables for subsequent iterations.
334 	 */
335 	for (i = 0; i <= repeats; i++) {
336 		/* Get the start time. */
337 		(void)gettimeofday(&start_time, NULL);
338 
339 		/*
340 		 * Insert records into the database. Depending on the type
341 		 * of the database, different types of keys are used. Calling
342 		 * different functions to perform insertions.
343 		 */
344 		if ((dbtype == DB_HEAP) && (ret = insert_heap(dbp, dbenv,
345 		    i == 0 ? DEF_INIT_RECS : recs_per_rep,
346 		    i == 0 ? 0 : (DEF_INIT_RECS + (i - 1) * recs_per_rep),
347 		    test_var)) != 0) {
348 			dbenv->err(dbenv, ret,
349 			    "Failed to insert records to heap database.");
350 			goto err;
351 		}
352 
353 		if ((dbtype == DB_BTREE) && (ret = insert_btree(dbp, dbenv,
354 		    i == 0 ? DEF_INIT_RECS : recs_per_rep,
355 		    i == 0 ? 0 : (DEF_INIT_RECS + (i - 1) * recs_per_rep),
356 		    test_var)) != 0) {
357 			dbenv->err(dbenv, ret,
358 			    "Failed to insert records to btree database.");
359 			goto err;
360 		}
361 
362 		/*
363 		 * Delete the same number of records except for the first
364 		 * iteration.
365 		 */
366 		if (i > 0 &&
367 		    (ret = delete_recs(dbp, dbenv, recs_per_rep)) != 0) {
368 			dbenv->err(dbenv, ret, "Failed to delete records.");
369 			goto err;
370 		}
371 
372 		/* Get the end time. */
373 		(void)gettimeofday(&end_time, NULL);
374 
375 		/* Calculate the runtime. */
376 		time_secs[i] =
377 		    (((double)end_time.tv_sec * NS_PER_MS +
378 		    end_time.tv_usec) -
379 		    ((double)start_time.tv_sec * NS_PER_MS +
380 		    start_time.tv_usec)) / NS_PER_MS;
381 
382 		/* Calculate the physical file size. */
383 		if ((ret = file_size(dbp, dbtype, &fsize)) != 0) {
384 			dbenv->err(dbenv, ret, "Failed to calculate "
385 			    "the file size on repeat %d.\n", i);
386 			goto err;
387 		}
388 		db_file_sizes[i] = fsize;
389 	}
390 	printf("\n------------------------------------------------------\n");
391 	printf("%5s \t %10s \t %10s\n", "repetition", "physical file size",
392 	    "running time");
393 	for (i = 0; i <= repeats; i++)
394 		printf("%5d \t\t %10d \t\t %.2f seconds\n",
395 		    i, db_file_sizes[i], time_secs[i]);
396 
397 err:
398 	if (db_file_sizes != NULL)
399 		free(db_file_sizes);
400 	if (time_secs != NULL)
401 		free(time_secs);
402 
403 	return (ret);
404 }
405 
406 /*
407  * file_size --
408  * 	Calculate the physical file size of the given database.
409  *
410  * Parameters:
411  *	dbp	the database handle
412  *	dbtype	the database type
413  *	fsize	the physical file size of database
414  */
415 int
file_size(dbp,dbtype,fsize)416 file_size(dbp, dbtype, fsize)
417 	DB *dbp;
418 	DBTYPE dbtype;
419 	int *fsize;
420 {
421 	DB_ENV *dbenv;		/* The environment handle. */
422 	u_int32_t pgcnt;	/* The number of pages in the database. */
423 	u_int32_t pgsize;	/* The page size. */
424 	int ret;		/* The return value. */
425 	int size;		/* The file size. */
426 	void *statp;		/* The statistic pointer for database size. */
427 
428 	dbenv = dbp->dbenv;
429 	pgsize = dbp->pgsize;
430 	ret = size = 0;
431 
432 	/* Retrieve the database's statistics. */
433 	if ((ret = dbp->stat(dbp, NULL, &statp, DB_FAST_STAT)) != 0) {
434 		dbenv->err(dbenv, ret, "DB->stat");
435 		return (ret);
436 	}
437 
438 	/* Get the page count. */
439 	pgcnt = (dbtype == DB_HEAP ? ((DB_HEAP_STAT *)statp)->heap_pagecnt :
440 	    ((DB_BTREE_STAT *)statp)->bt_pagecnt);
441 
442 	/* the file size is the value of page count multiplied by the page size. */
443 	size = pgcnt * pgsize;
444 	*fsize = size;
445 
446 	free(statp);
447 
448 	return (ret);
449 }
450 
451 /*
452  * insert_heap --
453  * 	Insert a specified number of records to a heap database,
454  *  with keys beginning with a specified value.
455  *
456  * Parameters:
457  *	dbp		the database handle
458  *	dbenv		the database environment
459  *	numrecs		the number of records to insert
460  *	start		the start position to insert records
461  *	test_var	test on variable-length data flag
462  */
463 
464 int
insert_heap(dbp,dbenv,numrecs,start,test_var)465 insert_heap(dbp, dbenv, numrecs, start, test_var)
466 	DB *dbp;
467 	DB_ENV *dbenv;
468 	int numrecs, start, test_var;
469 {
470 	DB_HEAP_RID rid;	/* The returned record id. */
471 	DBT key, data;		/* The key/data pair. */
472 	char buf[BUFFER_LEN];	/* The data buffer. */
473 	int cnt, ret;
474 
475 	memset(&rid, 0, sizeof(DB_HEAP_RID));
476 	memset(&key, 0, sizeof(DBT));
477 	memset(&data, 0, sizeof(DBT));
478 
479 	ret = 0;
480 
481 	/* For a heap database, the key must be an empty DB_HEAP_RID. */
482 	key.data = &rid;
483 	key.size = key.ulen = sizeof(DB_HEAP_RID);
484 	key.flags = DB_DBT_USERMEM;
485 	data.data = buf;
486 	data.flags = DB_DBT_USERMEM;
487 
488 	/*
489 	 * Insert a certain number of records into btree database,
490 	 * the data to insert is generated randomly.
491 	 */
492 	for (cnt = start; cnt < (numrecs + start) &&
493 	    (ret = generate_data(buf, cnt, test_var)) == 0; ++cnt) {
494 		data.size = data.ulen = (u_int32_t)strlen(buf) + 1;
495 
496 		/* Require DB_APPEND flag to add new data to the database.*/
497 		if ((ret = dbp->put(dbp, NULL, &key, &data, DB_APPEND)) != 0) {
498 			dbenv->err(dbenv, ret, "insert_heap:DB->put");
499 			break;
500 		}
501 	}
502 
503 	return (ret);
504 }
505 
506 /*
507  * insert_btree --
508  * 	Insert a specified number of records to a btree database,
509  *  with keys beginning with a specified value.
510  *
511  * Parameters:
512  *	dbp		the database handle
513  *	dbenv		the database environment
514  *	numrecs		the number of records to insert
515  *	start		the start position to insert records
516  *	test_var	test on variable-length data flag
517  */
518 
519 int
insert_btree(dbp,dbenv,numrecs,start,test_var)520 insert_btree(dbp, dbenv, numrecs, start, test_var)
521 	DB *dbp;
522 	DB_ENV *dbenv;
523 	int numrecs, start, test_var;
524 {
525 	DBT key, data;
526 	char buf[BUFFER_LEN];
527 	int cnt, ret;
528 
529 	memset(&key, 0, sizeof(DBT));
530 	memset(&data, 0, sizeof(DBT));
531 
532 	ret = 0;
533 
534 	key.data = &cnt;
535 	key.size = key.ulen = sizeof(int);
536 	key.flags = DB_DBT_USERMEM;
537 	data.data = buf;
538 	data.flags = DB_DBT_USERMEM;
539 
540 	/*
541 	 * Insert a certain number of records into btree database,
542 	 * the data to insert is generated by randomly.
543 	 */
544 	for (cnt = start; cnt < (numrecs + start) &&
545 	    (ret = generate_data(buf, cnt, test_var)) == 0; ++cnt) {
546 		data.size = data.ulen = (u_int32_t)strlen(buf) + 1;
547 
548 		if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0) {
549 			dbenv->err(dbenv, ret, "insert_btree:DB->put");
550 			break;
551 		}
552 	}
553 
554 	return (ret);
555 }
556 
557 /*
558  * generate_data --
559  * 	Generate data for the specified record.
560  *
561  * Parameters:
562  *	buf		the buffer variable to hold data
563  *	rec_no		the record number
564  *	test_var	test on variable-length data flag
565  */
566 
567 int
generate_data(buf,rec_no,test_var)568 generate_data(buf, rec_no, test_var)
569 	char *buf;
570 	int rec_no, test_var;
571 {
572 	const char *str = "abcdefghijklmnopqrst";
573 	int len = (int)strlen(str);
574 
575 	/*
576 	 * Default use the fix-length data,
577 	 * if required then use variable-length data.
578 	 */
579 	if (test_var == 1)
580 		len = rand() % (len - 2) + 1;
581 
582 	(void)sprintf(buf, "%04d_%*s", rec_no, len, str);
583 
584 	return (0);
585 }
586 
587 /*
588  * delete_recs --
589  * 	Delete a specified number of records.
590  *
591  * Parameters:
592  *	dbp	the database handle
593  *	dbenv	the database environment handle
594  *	numrecs	number of records to delete
595  */
596 
597 int
delete_recs(dbp,dbenv,numrecs)598 delete_recs(dbp, dbenv, numrecs)
599 	DB *dbp;
600 	DB_ENV *dbenv;
601 	int numrecs;
602 {
603 	DBC *dbcp;
604 	DBT key, data;
605 	int cnt, ret;
606 
607 	memset(&key, 0, sizeof(DBT));
608 	memset(&data, 0, sizeof(DBT));
609 
610 	dbcp = NULL;
611 	cnt = ret = 0;
612 
613 	/* Create a cursor to delete records. */
614 	if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
615 		dbenv->err(dbenv, ret, "delete_recs:DB->cursor");
616 		goto err;
617 	}
618 
619 	/* Delete the first numrecs records. */
620 	while ((ret = dbcp->get(dbcp, &key, &data, DB_NEXT)) == 0 &&
621 	    cnt < numrecs) {
622 		if ((ret = dbcp->del(dbcp, 0)) != 0) {
623 			dbenv->err(dbenv, ret, "delete_recs:DBCursor->del");
624 			break;
625 		} else
626 			++cnt;
627 	}
628 
629 err:
630 	if (dbcp != NULL && (ret = dbcp->close(dbcp)) != 0)
631 		dbenv->err(dbenv, ret, "delete_recs:DBCursor->close");
632 
633 	return (ret);
634 }
635 
636 /*
637  * usage --
638  *	Describe this program's command line options, then exit.
639  */
640 void
usage()641 usage()
642 {
643 	fprintf(stderr, "usage: %s:\n%s \n %s\n", progname,
644 	    "\t[-b][-c cachesize][-d] -h home [-n recs_per_rep]",
645 	    "\t[-p pgsize][-r repeats][-S ghpsize][-s hpsize]");
646 
647 	fprintf(stderr, "-b: run sample application using a btree database.\n");
648 	fprintf(stderr, "-c: specify the cache size for the environment.\n");
649 	fprintf(stderr, "-d: test on variable-length data "
650 	    "(default: fix-length).\n");
651 	fprintf(stderr, "-h: specify the home directory for "
652 	    "the environment (required).\n");
653 	fprintf(stderr, "-n: specify the num. of records "
654 	    "per repetition (default: %d).\n", DEF_RECS_PER_REP);
655 	fprintf(stderr, "-p: specify the pgsize of database.\n");
656 	fprintf(stderr, "-r: number of repetition (a pair of "
657 	    "insertion and deletion (default: %d)).\n", DEF_REPEATS);
658 	fprintf(stderr,
659 	    "-S: specify the heap size (gbytes) for the heap database.\n");
660 	fprintf(stderr,
661 	    "-s: specify the heap size (bytes) for the heap database.\n");
662 
663 	exit(EXIT_FAILURE);
664 }
665 
666 /*
667  * open_env --
668  * 	Open the environment before insert/delete operation
669  *
670  * Parameters:
671  *	dbenvp		the database environment handle
672  *	home		the home directory for the environment
673  *	cachesize	the cache size for the environment
674  */
675 
676 int
open_env(dbenvp,home,cachesize)677 open_env(dbenvp, home, cachesize)
678 	DB_ENV **dbenvp;
679 	char *home;
680 	u_int32_t cachesize;
681 {
682 	DB_ENV *dbenv;
683 	int ret = 0;
684 
685 	/* Create an environment handle and open an environment. */
686 	if ((ret = db_env_create(&dbenv, 0)) != 0) {
687 		fprintf(stderr, "%s: db_env_create: %s\n",
688 		    progname, db_strerror(ret));
689 		return (ret);
690 	}
691 
692 	*dbenvp = dbenv;
693 
694 	/*
695 	 * Prefix any error messages with the name of this program and a ':'.
696 	 * Setting the errfile to stderr is not necessary, since that is the
697 	 * default; it is provided here as a placeholder showing where one
698 	 * could direct error messages to an application-specific log file.
699 	 */
700 	dbenv->set_errfile(dbenv, stderr);
701 	dbenv->set_errpfx(dbenv, progname);
702 
703 	/* Set cachesize for the database environment. */
704 	if ((cachesize > 0) && (ret =
705 	    dbenv->set_cachesize(dbenv, (u_int32_t)0, cachesize, 1)) != 0) {
706 		dbenv->err(dbenv, ret, "DB_ENV->set_cachesize");
707 		return (ret);
708 	}
709 
710 	if ((ret = dbenv->open(dbenv, home, DB_CREATE | DB_INIT_MPOOL, 0)) != 0)
711 		dbenv->err(dbenv, ret, "DB_ENV->open");
712 
713 	return (ret);
714 }
715 
716 /*
717  * open_db --
718  * 	Open the database before insert/delete operation.
719  *
720  * Parameters:
721  *	dbpp	the database handle
722  *	dbenv	the database environment
723  *	dbtype	the database type
724  *	home	the home directory for the environment
725  *	gphsize	the heap size (gbytes) for the heap database
726  *	hpsize	the heap size (bytes) for the heap database
727  *	pgsize	the page size for the heap database
728  */
729 int
open_db(dbpp,dbenv,dbtype,home,ghpsize,hpsize,pgsize)730 open_db(dbpp, dbenv, dbtype, home, ghpsize, hpsize, pgsize)
731 	DB **dbpp;
732 	DB_ENV *dbenv;
733 	DBTYPE dbtype;
734 	char *home;
735 	u_int32_t ghpsize, hpsize, pgsize;
736 {
737 	DB *dbp;
738 	u_int32_t dbflags = 0;
739 	char *dbname;
740 	int ret = 0;
741 
742 	dbname = (dbtype == DB_HEAP) ? "heap.db" : "btree.db";
743 
744 	/* Create a database handle and open a database. */
745 	if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
746 		dbenv->err(dbenv, ret, "db_create : %s", dbname);
747 		goto err;
748 	}
749 
750 	*dbpp = dbp;
751 
752 	/* Set the record compare function for the btree database. */
753 	if ((dbtype == DB_BTREE) &&
754 	    (ret = dbp->set_bt_compare(dbp, compare_int)) != 0) {
755 		dbp->err(dbp, ret, "DB->set_bt_compare");
756 		goto err;
757 	}
758 
759 	/* Set heap size for the heap database. */
760 	if ((dbtype == DB_HEAP) && (ghpsize > 0 || hpsize > 0) &&
761 	    (ret = dbp->set_heapsize(dbp, ghpsize, hpsize, 0)) != 0) {
762 		dbenv->err(dbenv, ret, "DB->set_heapsize");
763 		return (ret);
764 	}
765 	/* Set page size for the database. */
766 	if ((pgsize > 0) && (ret = dbp->set_pagesize(dbp, pgsize)) != 0) {
767 		dbenv->err(dbenv, ret, "DB->set_pagesize");
768 		return (ret);
769 	}
770 
771 	if ((ret =
772 	    dbp->open(dbp, NULL, dbname, NULL, dbtype, DB_CREATE, 0)) != 0)
773 		dbenv->err(dbenv, ret, "DB->open");
774 err:
775 
776 	return (ret);
777 }
778 
779 /*
780  * compare_int --
781  * 	Compare two data value for btree database.
782  *
783  * Parameters:
784  *	dbp	the database handle
785  *	a	one of the data value use to compare
786  *	b	another data value use to compare
787  *	locp	lock handle
788  */
789 int
compare_int(dbp,a,b,locp)790 compare_int(dbp, a, b, locp)
791 	DB *dbp;
792 	const DBT *a, *b;
793 	size_t *locp;
794 {
795 	int ai, bi;
796 
797 	dbp = NULL;
798 	locp = NULL;
799 
800 	/*
801 	 * Returns:
802 	 *	< 0 if a < b
803 	 *	= 0 if a = b
804 	 *	> 0 if a > b
805 	 */
806 	memcpy(&ai, a->data, sizeof(int));
807 	memcpy(&bi, b->data, sizeof(int));
808 	return (ai - bi);
809 }
810