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