1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1998, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 #include "dbinc/db_page.h"
13 #include "dbinc/heap.h"
14 #include "dbinc/lock.h"
15 #include "dbinc/mp.h"
16 
17 /*
18  * __heap_truncate --
19  *	Truncate a database.
20  *
21  * PUBLIC: int __heap_truncate __P((DBC *, u_int32_t *));
22  */
23 int
__heap_truncate(dbc,countp)24 __heap_truncate(dbc, countp)
25 	DBC *dbc;
26 	u_int32_t *countp;
27 {
28 	DB *dbp;
29 	DB_LOCK lock, meta_lock;
30 	DB_MPOOLFILE *mpf;
31 	DBT log_dbt;
32 	HEAPHDR *hdr;
33 	HEAPMETA *meta;
34 	HEAPPG *pg;
35 	db_pgno_t pgno;
36 	int i, ret, t_ret;
37 	u_int32_t count, next_region, region_size;
38 
39 	LOCK_INIT(lock);
40 	dbp = dbc->dbp;
41 	mpf = dbp->mpf;
42 	count = 0;
43 	next_region = FIRST_HEAP_RPAGE;
44 	region_size = HEAP_REGION_SIZE(dbp);
45 
46 	/* Traverse the entire database, starting with the metadata pg. */
47 	pgno = PGNO_BASE_MD;
48 	if ((ret = __db_lget(dbc,
49 	    LCK_ALWAYS, pgno, DB_LOCK_WRITE, 0, &meta_lock)) != 0)
50 		return (ret);
51 	if ((ret = __memp_fget(mpf, &pgno,
52 	    dbc->thread_info, dbc->txn, DB_MPOOL_DIRTY, &meta)) != 0) {
53 		__TLPUT(dbc, lock);
54 		goto err;
55 	}
56 
57 	for (;;) {
58 		pgno++;
59 		if ((ret = __db_lget(dbc,
60 		    LCK_COUPLE, pgno, DB_LOCK_WRITE, 0, &lock)) != 0)
61 			break;
62 		if ((ret = __memp_fget(mpf, &pgno,
63 		    dbc->thread_info, dbc->txn, DB_MPOOL_DIRTY, &pg)) != 0) {
64 			if (ret == DB_PAGE_NOTFOUND)
65 				ret = 0;
66 			break;
67 		}
68 		if (DBC_LOGGING(dbc)) {
69 			memset(&log_dbt, 0, sizeof(DBT));
70 			log_dbt.data = pg;
71 			log_dbt.size = dbp->pgsize;
72 			if ((ret = __heap_trunc_page_log(dbp, dbc->txn,
73 			    &LSN(pg), 0, pgno,
74 			    &log_dbt, (pgno == next_region), &LSN(pg))) != 0)
75 				goto err;
76 		} else
77 			LSN_NOT_LOGGED(LSN(pg));
78 
79 		if (pgno == next_region) {
80 			DB_ASSERT(dbp->env, TYPE(pg) == P_IHEAP);
81 			next_region += region_size + 1;
82 		} else {
83 			/*
84 			 * We can't use pg->entries to calculate the record
85 			 * count, because it can include split records.  So we
86 			 * check the header for each entry and only count
87 			 * non-split records and the first piece of split
88 			 * records. But if the page is empty, there's no work to
89 			 * do.
90 			 */
91 			if (NUM_ENT(pg) != 0)
92 				for (i = 0; i <= HEAP_HIGHINDX(pg); i++) {
93 					if (HEAP_OFFSETTBL(dbp, pg)[i] == 0)
94 						continue;
95 					hdr = (HEAPHDR *)P_ENTRY(dbp, pg, i);
96 					if (!F_ISSET(hdr, HEAP_RECSPLIT) ||
97 					    F_ISSET(hdr, HEAP_RECFIRST))
98 						count++;
99 				}
100 		}
101 		if ((ret = __memp_fput(mpf,
102 		    dbc->thread_info, pg, dbc->priority)) != 0)
103 			break;
104 		if ((ret = __memp_fget(mpf, &pgno,
105 		    dbc->thread_info, dbc->txn, DB_MPOOL_FREE, &pg)) != 0)
106 			break;
107 	}
108 	if ((t_ret = __TLPUT(dbc, lock)) != 0 && ret == 0)
109 		ret = t_ret;
110 
111 	if (countp != NULL && ret == 0)
112 		*countp = count;
113 
114 	if (DBC_LOGGING(dbc)) {
115 		if ((ret = __heap_trunc_meta_log(dbp, dbc->txn, &LSN(meta), 0,
116 		    meta->dbmeta.pgno, meta->dbmeta.last_pgno,
117 		    meta->dbmeta.key_count, meta->dbmeta.record_count,
118 		    meta->curregion, meta->nregions, &LSN(meta))) != 0)
119 			goto err;
120 	} else
121 		LSN_NOT_LOGGED(LSN(meta));
122 	meta->dbmeta.key_count = 0;
123 	meta->dbmeta.record_count = 0;
124 	meta->dbmeta.last_pgno = PGNO_BASE_MD + 1;
125 	meta->curregion = 1;
126 	meta->nregions = 1;
127 
128 	if ((ret = __memp_ftruncate(mpf, dbc->txn,
129 	    dbc->thread_info, PGNO_BASE_MD + 1, MP_TRUNC_NOCACHE)) != 0)
130 		goto err;
131 
132 	/* Create the first region. */
133 	pgno = PGNO_BASE_MD + 1;
134 	if ((ret = __memp_fget(mpf, &pgno, dbc->thread_info,
135 	    dbc->txn, DB_MPOOL_CREATE | DB_MPOOL_DIRTY, &pg)) != 0)
136 		goto err;
137 
138 	memset(pg, 0, dbp->pgsize);
139 	P_INIT(pg,
140 	    dbp->pgsize, 1, PGNO_INVALID, PGNO_INVALID, 0, P_IHEAP);
141 	ret = __db_log_page(dbp, dbc->txn, &pg->lsn, pgno, (PAGE *)pg);
142 	if ((t_ret = __memp_fput(
143 	    mpf, dbc->thread_info, pg, dbp->priority)) != 0 && ret == 0)
144 		ret = t_ret;
145 
146 err:	if ((t_ret = __memp_fput(mpf,
147 	    dbc->thread_info, meta, dbc->priority)) != 0 && ret == 0)
148 		ret = t_ret;
149 	if ((t_ret = __TLPUT(dbc, meta_lock)) && ret == 0)
150 		ret = t_ret;
151 	return (ret);
152 }
153