1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2001,2008 Oracle. All rights reserved. 5 * 6 * $Id: db_truncate.c,v 12.30 2008/03/18 17:13:16 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/hash.h" 15#include "dbinc/qam.h" 16#include "dbinc/lock.h" 17#include "dbinc/log.h" 18#include "dbinc/txn.h" 19 20static int __db_cursor_check __P((DB *)); 21 22/* 23 * __db_truncate_pp 24 * DB->truncate pre/post processing. 25 * 26 * PUBLIC: int __db_truncate_pp __P((DB *, DB_TXN *, u_int32_t *, u_int32_t)); 27 */ 28int 29__db_truncate_pp(dbp, txn, countp, flags) 30 DB *dbp; 31 DB_TXN *txn; 32 u_int32_t *countp, flags; 33{ 34 DB_THREAD_INFO *ip; 35 ENV *env; 36 int handle_check, ret, t_ret, txn_local; 37 38 env = dbp->env; 39 handle_check = txn_local = 0; 40 41 STRIP_AUTO_COMMIT(flags); 42 43 /* Check for invalid flags. */ 44 if (F_ISSET(dbp, DB_AM_SECONDARY)) { 45 __db_errx(env, "DB->truncate forbidden on secondary indices"); 46 return (EINVAL); 47 } 48 if ((ret = __db_fchk(env, "DB->truncate", flags, 0)) != 0) 49 return (ret); 50 51 ENV_ENTER(env, ip); 52 53 /* 54 * Make sure there are no active cursors on this db. Since we drop 55 * pages we cannot really adjust cursors. 56 */ 57 if ((ret = __db_cursor_check(dbp)) != 0) { 58 __db_errx(env, 59 "DB->truncate not permitted with active cursors"); 60 goto err; 61 } 62 63#ifdef CONFIG_TEST 64 if (IS_REP_MASTER(env)) 65 DB_TEST_WAIT(env, env->test_check); 66#endif 67 /* Check for replication block. */ 68 handle_check = IS_ENV_REPLICATED(env); 69 if (handle_check && 70 (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0) { 71 handle_check = 0; 72 goto err; 73 } 74 75 /* 76 * Check for changes to a read-only database. This must be after the 77 * replication block so that we cannot race master/client state changes. 78 */ 79 if (DB_IS_READONLY(dbp)) { 80 ret = __db_rdonly(env, "DB->truncate"); 81 goto err; 82 } 83 84 /* 85 * Create local transaction as necessary, check for consistent 86 * transaction usage. 87 */ 88 if (IS_DB_AUTO_COMMIT(dbp, txn)) { 89 if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0) 90 goto err; 91 txn_local = 1; 92 } 93 94 /* Check for consistent transaction usage. */ 95 if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0) 96 goto err; 97 98 ret = __db_truncate(dbp, ip, txn, countp); 99 100err: if (txn_local && 101 (t_ret = __db_txn_auto_resolve(env, txn, 0, ret)) && ret == 0) 102 ret = t_ret; 103 104 /* Release replication block. */ 105 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0) 106 ret = t_ret; 107 108 ENV_LEAVE(env, ip); 109 return (ret); 110} 111 112/* 113 * __db_truncate 114 * DB->truncate. 115 * 116 * PUBLIC: int __db_truncate __P((DB *, DB_THREAD_INFO *, DB_TXN *, 117 * PUBLIC: u_int32_t *)); 118 */ 119int 120__db_truncate(dbp, ip, txn, countp) 121 DB *dbp; 122 DB_THREAD_INFO *ip; 123 DB_TXN *txn; 124 u_int32_t *countp; 125{ 126 DB *sdbp; 127 DBC *dbc; 128 ENV *env; 129 u_int32_t scount; 130 int ret, t_ret; 131 132 env = dbp->env; 133 dbc = NULL; 134 ret = 0; 135 136 /* 137 * Run through all secondaries and truncate them first. The count 138 * returned is the count of the primary only. QUEUE uses normal 139 * processing to truncate so it will update the secondaries normally. 140 */ 141 if (dbp->type != DB_QUEUE && LIST_FIRST(&dbp->s_secondaries) != NULL) { 142 if ((ret = __db_s_first(dbp, &sdbp)) != 0) 143 return (ret); 144 for (; sdbp != NULL && ret == 0; ret = __db_s_next(&sdbp, txn)) 145 if ((ret = __db_truncate(sdbp, ip, txn, &scount)) != 0) 146 break; 147 if (sdbp != NULL) 148 (void)__db_s_done(sdbp, txn); 149 if (ret != 0) 150 return (ret); 151 } 152 153 DB_TEST_RECOVERY(dbp, DB_TEST_PREDESTROY, ret, NULL); 154 155 /* Acquire a cursor. */ 156 if ((ret = __db_cursor(dbp, ip, txn, &dbc, 0)) != 0) 157 return (ret); 158 159 DEBUG_LWRITE(dbc, txn, "DB->truncate", NULL, NULL, 0); 160 161 switch (dbp->type) { 162 case DB_BTREE: 163 case DB_RECNO: 164 ret = __bam_truncate(dbc, countp); 165 break; 166 case DB_HASH: 167 ret = __ham_truncate(dbc, countp); 168 break; 169 case DB_QUEUE: 170 ret = __qam_truncate(dbc, countp); 171 break; 172 case DB_UNKNOWN: 173 default: 174 ret = __db_unknown_type(env, "DB->truncate", dbp->type); 175 break; 176 } 177 178 /* Discard the cursor. */ 179 if (dbc != NULL && (t_ret = __dbc_close(dbc)) != 0 && ret == 0) 180 ret = t_ret; 181 182 DB_TEST_RECOVERY(dbp, DB_TEST_POSTDESTROY, ret, NULL); 183 184DB_TEST_RECOVERY_LABEL 185 186 return (ret); 187} 188 189/* 190 * __db_cursor_check -- 191 * See if there are any active cursors on this db. 192 */ 193static int 194__db_cursor_check(dbp) 195 DB *dbp; 196{ 197 DB *ldbp; 198 DBC *dbc; 199 ENV *env; 200 int found; 201 202 env = dbp->env; 203 204 MUTEX_LOCK(env, env->mtx_dblist); 205 FIND_FIRST_DB_MATCH(env, dbp, ldbp); 206 for (found = 0; 207 !found && ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid; 208 ldbp = TAILQ_NEXT(ldbp, dblistlinks)) { 209 MUTEX_LOCK(env, dbp->mutex); 210 TAILQ_FOREACH(dbc, &ldbp->active_queue, links) 211 if (IS_INITIALIZED(dbc)) { 212 found = 1; 213 break; 214 } 215 MUTEX_UNLOCK(env, dbp->mutex); 216 } 217 MUTEX_UNLOCK(env, env->mtx_dblist); 218 219 return (found ? EINVAL : 0); 220} 221