1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_reclaim.c,v 12.16 2008/03/12 20:33:20 mbrey Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/btree.h"
14#include "dbinc/mp.h"
15
16/*
17 * __db_traverse_big
18 *	Traverse a chain of overflow pages and call the callback routine
19 * on each one.  The calling convention for the callback is:
20 *	callback(dbc, page, cookie, did_put),
21 * where did_put is a return value indicating if the page in question has
22 * already been returned to the mpool.
23 *
24 * PUBLIC: int __db_traverse_big __P((DBC *, db_pgno_t,
25 * PUBLIC:	int (*)(DBC *, PAGE *, void *, int *), void *));
26 */
27int
28__db_traverse_big(dbc, pgno, callback, cookie)
29	DBC *dbc;
30	db_pgno_t pgno;
31	int (*callback) __P((DBC *, PAGE *, void *, int *));
32	void *cookie;
33{
34	DB_MPOOLFILE *mpf;
35	PAGE *p;
36	int did_put, ret;
37
38	mpf = dbc->dbp->mpf;
39
40	do {
41		did_put = 0;
42		if ((ret = __memp_fget(mpf,
43		     &pgno, dbc->thread_info, dbc->txn, 0, &p)) != 0)
44			return (ret);
45		/*
46		 * If we are freeing pages only process the overflow
47		 * chain if the head of the chain has a refcount of 1.
48		 */
49		pgno = NEXT_PGNO(p);
50		if (callback == __db_truncate_callback && OV_REF(p) != 1)
51			pgno = PGNO_INVALID;
52		if ((ret = callback(dbc, p, cookie, &did_put)) == 0 &&
53		    !did_put)
54			ret = __memp_fput(mpf,
55			     dbc->thread_info, p, dbc->priority);
56	} while (ret == 0 && pgno != PGNO_INVALID);
57
58	return (ret);
59}
60
61/*
62 * __db_reclaim_callback
63 * This is the callback routine used during a delete of a subdatabase.
64 * we are traversing a btree or hash table and trying to free all the
65 * pages.  Since they share common code for duplicates and overflow
66 * items, we traverse them identically and use this routine to do the
67 * actual free.  The reason that this is callback is because hash uses
68 * the same traversal code for statistics gathering.
69 *
70 * PUBLIC: int __db_reclaim_callback __P((DBC *, PAGE *, void *, int *));
71 */
72int
73__db_reclaim_callback(dbc, p, cookie, putp)
74	DBC *dbc;
75	PAGE *p;
76	void *cookie;
77	int *putp;
78{
79	DB *dbp;
80	int ret;
81
82	COMPQUIET(cookie, NULL);
83	dbp = dbc->dbp;
84
85	/*
86	 * We don't want to log the free of the root with the subdb.
87	 * If we abort then the subdb may not be openable to undo
88	 * the free.
89	 */
90	if ((dbp->type == DB_BTREE || dbp->type == DB_RECNO) &&
91	    PGNO(p) == ((BTREE *)dbp->bt_internal)->bt_root)
92		return (0);
93	if ((ret = __db_free(dbc, p)) != 0)
94		return (ret);
95	*putp = 1;
96
97	return (0);
98}
99
100/*
101 * __db_truncate_callback
102 * This is the callback routine used during a truncate.
103 * we are traversing a btree or hash table and trying to free all the
104 * pages.
105 *
106 * PUBLIC: int __db_truncate_callback __P((DBC *, PAGE *, void *, int *));
107 */
108int
109__db_truncate_callback(dbc, p, cookie, putp)
110	DBC *dbc;
111	PAGE *p;
112	void *cookie;
113	int *putp;
114{
115	DB *dbp;
116	DBT ddbt, ldbt;
117	DB_MPOOLFILE *mpf;
118	db_indx_t indx, len, off, tlen, top;
119	u_int8_t *hk, type;
120	u_int32_t *countp;
121	int ret;
122
123	top = NUM_ENT(p);
124	dbp = dbc->dbp;
125	mpf = dbp->mpf;
126	countp = cookie;
127	*putp = 1;
128
129	switch (TYPE(p)) {
130	case P_LBTREE:
131		/* Skip for off-page duplicates and deleted items. */
132		for (indx = 0; indx < top; indx += P_INDX) {
133			type = GET_BKEYDATA(dbp, p, indx + O_INDX)->type;
134			if (!B_DISSET(type) && B_TYPE(type) != B_DUPLICATE)
135				++*countp;
136		}
137		/* FALLTHROUGH */
138	case P_IBTREE:
139	case P_IRECNO:
140	case P_INVALID:
141		if (dbp->type != DB_HASH &&
142		    ((BTREE *)dbp->bt_internal)->bt_root == PGNO(p)) {
143			type = dbp->type == DB_RECNO ? P_LRECNO : P_LBTREE;
144			goto reinit;
145		}
146		break;
147	case P_OVERFLOW:
148		if ((ret = __memp_dirty(mpf,
149		    &p, dbc->thread_info, dbc->txn, dbc->priority, 0)) != 0)
150			return (ret);
151		if (DBC_LOGGING(dbc)) {
152			if ((ret = __db_ovref_log(dbp, dbc->txn,
153			    &LSN(p), 0, p->pgno, -1, &LSN(p))) != 0)
154				return (ret);
155		} else
156			LSN_NOT_LOGGED(LSN(p));
157		if (--OV_REF(p) != 0)
158			*putp = 0;
159		break;
160	case P_LRECNO:
161		for (indx = 0; indx < top; indx += O_INDX) {
162			type = GET_BKEYDATA(dbp, p, indx)->type;
163			if (!B_DISSET(type))
164				++*countp;
165		}
166
167		if (((BTREE *)dbp->bt_internal)->bt_root == PGNO(p)) {
168			type = P_LRECNO;
169			goto reinit;
170		}
171		break;
172	case P_LDUP:
173		/* Correct for deleted items. */
174		for (indx = 0; indx < top; indx += O_INDX)
175			if (!B_DISSET(GET_BKEYDATA(dbp, p, indx)->type))
176				++*countp;
177
178		break;
179	case P_HASH:
180		/* Correct for on-page duplicates and deleted items. */
181		for (indx = 0; indx < top; indx += P_INDX) {
182			switch (*H_PAIRDATA(dbp, p, indx)) {
183			case H_OFFDUP:
184				break;
185			case H_OFFPAGE:
186			case H_KEYDATA:
187				++*countp;
188				break;
189			case H_DUPLICATE:
190				tlen = LEN_HDATA(dbp, p, 0, indx);
191				hk = H_PAIRDATA(dbp, p, indx);
192				for (off = 0; off < tlen;
193				    off += len + 2 * sizeof(db_indx_t)) {
194					++*countp;
195					memcpy(&len,
196					    HKEYDATA_DATA(hk)
197					    + off, sizeof(db_indx_t));
198				}
199				break;
200			default:
201				return (__db_pgfmt(dbp->env, p->pgno));
202			}
203		}
204		/* Don't free the head of the bucket. */
205		if (PREV_PGNO(p) == PGNO_INVALID) {
206			type = P_HASH;
207
208reinit:			if ((ret = __memp_dirty(mpf, &p,
209			    dbc->thread_info, dbc->txn, dbc->priority, 0)) != 0)
210				return (ret);
211			*putp = 0;
212			if (DBC_LOGGING(dbc)) {
213				memset(&ldbt, 0, sizeof(ldbt));
214				memset(&ddbt, 0, sizeof(ddbt));
215				ldbt.data = p;
216				ldbt.size = P_OVERHEAD(dbp);
217				ldbt.size += p->entries * sizeof(db_indx_t);
218				ddbt.data = (u_int8_t *)p + HOFFSET(p);
219				ddbt.size = dbp->pgsize - HOFFSET(p);
220				if ((ret = __db_pg_init_log(dbp,
221				    dbc->txn, &LSN(p), 0,
222				    p->pgno, &ldbt, &ddbt)) != 0)
223					return (ret);
224			} else
225				LSN_NOT_LOGGED(LSN(p));
226
227			P_INIT(p, dbp->pgsize, PGNO(p), PGNO_INVALID,
228			    PGNO_INVALID, type == P_HASH ? 0 : 1, type);
229		}
230		break;
231	default:
232		return (__db_pgfmt(dbp->env, p->pgno));
233	}
234
235	if (*putp == 1) {
236		if ((ret = __db_free(dbc, p)) != 0)
237			return (ret);
238	} else {
239		if ((ret = __memp_fput(mpf, dbc->thread_info, p,
240		    dbc->priority)) != 0)
241			return (ret);
242		*putp = 1;
243	}
244
245	return (0);
246}
247