1 /*-
2 * Copyright (c) 2001, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file LICENSE for license information.
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/btree.h"
14 #include "dbinc/hash.h"
15 #include "dbinc/heap.h"
16 #include "dbinc/qam.h"
17 #include "dbinc/lock.h"
18 #include "dbinc/partition.h"
19 #include "dbinc/txn.h"
20
21 static int __db_cursor_check_func
22 __P((DBC *, DBC *, u_int32_t *, db_pgno_t, u_int32_t, void *));
23 static int __db_cursor_check __P((DB *));
24
25 /*
26 * __db_truncate_pp
27 * DB->truncate pre/post processing.
28 *
29 * countp can be NULL.
30
31 * PUBLIC: int __db_truncate_pp __P((DB *, DB_TXN *, u_int32_t *, u_int32_t));
32 */
33 int
__db_truncate_pp(dbp,txn,countp,flags)34 __db_truncate_pp(dbp, txn, countp, flags)
35 DB *dbp;
36 DB_TXN *txn;
37 u_int32_t *countp, flags;
38 {
39 DB_THREAD_INFO *ip;
40 ENV *env;
41 int handle_check, ret, t_ret, txn_local;
42 #ifdef HAVE_SLICES
43 u_int32_t slice_txn_flags;
44 #endif
45
46 env = dbp->env;
47 handle_check = txn_local = 0;
48 #ifdef HAVE_SLICES
49 slice_txn_flags = flags;
50 #endif
51
52 DB_ILLEGAL_BEFORE_OPEN(dbp, "DB->truncate");
53
54 STRIP_AUTO_COMMIT(flags);
55
56 /* Check for invalid flags. */
57 if (F_ISSET(dbp, DB_AM_SECONDARY)) {
58 __db_errx(env, DB_STR("0685",
59 "DB->truncate forbidden on secondary indices"));
60 return (EINVAL);
61 }
62 if ((ret = __db_fchk(env, "DB->truncate", flags, 0)) != 0)
63 return (ret);
64
65 ENV_ENTER(env, ip);
66 XA_CHECK_TXN(ip, txn);
67
68 /*
69 * Make sure there are no active cursors on this db. Since we drop
70 * pages we cannot really adjust cursors.
71 */
72 if ((ret = __db_cursor_check(dbp)) != 0) {
73 __db_errx(env, DB_STR("0686",
74 "DB->truncate not permitted with active cursors"));
75 goto err;
76 }
77
78 #ifdef CONFIG_TEST
79 if (IS_REP_MASTER(env))
80 DB_TEST_WAIT(env, env->test_check);
81 #endif
82 /* Check for replication block. */
83 handle_check = IS_ENV_REPLICATED(env);
84 if (handle_check &&
85 (ret = __db_rep_enter(dbp, 1, 0, IS_REAL_TXN(txn))) != 0) {
86 handle_check = 0;
87 goto err;
88 }
89
90 /*
91 * Check for changes to a read-only database. This must be after the
92 * replication block so that we cannot race master/client state changes.
93 */
94 if (DB_IS_READONLY(dbp)) {
95 ret = __db_rdonly(env, "DB->truncate");
96 goto err;
97 }
98
99 /*
100 * Create local transaction as necessary, check for consistent
101 * transaction usage.
102 */
103 if (IS_DB_AUTO_COMMIT(dbp, txn)) {
104 if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0)
105 goto err;
106 txn_local = 1;
107 }
108
109 /* Check for consistent transaction usage. */
110 if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0)
111 goto err;
112
113 #ifdef HAVE_SLICES
114 /*
115 * If sliced, just truncate the slices. The 'else' explicitly avoids
116 * truncating the container of a sliced db; it might contain metadata
117 * such as the location of the slices, etc.
118 */
119 if (FLD_ISSET(dbp->open_flags, DB_SLICED))
120 ret = __db_slice_truncate(dbp, txn, countp, slice_txn_flags);
121 else
122 #endif
123 /*lint -e{539} Did not expect positive indentation. */
124 ret = __db_truncate(dbp, ip, txn, countp);
125
126 err: if (txn_local &&
127 (t_ret = __db_txn_auto_resolve(env, txn, 0, ret)) && ret == 0)
128 ret = t_ret;
129
130 /* Release replication block. */
131 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
132 ret = t_ret;
133
134 ENV_LEAVE(env, ip);
135 return (ret);
136 }
137
138 /*
139 * __db_truncate
140 * DB->truncate.
141 *
142 * PUBLIC: int __db_truncate __P((DB *, DB_THREAD_INFO *, DB_TXN *,
143 * PUBLIC: u_int32_t *));
144 */
145 int
__db_truncate(dbp,ip,txn,countp)146 __db_truncate(dbp, ip, txn, countp)
147 DB *dbp;
148 DB_THREAD_INFO *ip;
149 DB_TXN *txn;
150 u_int32_t *countp;
151 {
152 DB *sdbp;
153 DBC *dbc;
154 ENV *env;
155 u_int32_t scount;
156 int ret, t_ret;
157
158 env = dbp->env;
159 dbc = NULL;
160 ret = 0;
161
162 /*
163 * Run through all secondaries and truncate them first. The count
164 * returned is the count of the primary only. QUEUE uses normal
165 * processing to truncate so it will update the secondaries normally.
166 */
167 if (dbp->type != DB_QUEUE && DB_IS_PRIMARY(dbp)) {
168 if ((ret = __db_s_first(dbp, &sdbp)) != 0)
169 return (ret);
170 for (; sdbp != NULL && ret == 0; ret = __db_s_next(&sdbp, txn))
171 if ((ret = __db_truncate(sdbp, ip, txn, &scount)) != 0)
172 break;
173 if (sdbp != NULL)
174 (void)__db_s_done(sdbp, txn);
175 if (ret != 0)
176 return (ret);
177 }
178
179 DB_TEST_RECOVERY(dbp, DB_TEST_PREDESTROY, ret, NULL);
180
181 /* Acquire a cursor. */
182 if ((ret = __db_cursor(dbp, ip, txn, &dbc, 0)) != 0)
183 return (ret);
184
185 DEBUG_LWRITE(dbc, txn, "DB->truncate", NULL, NULL, 0);
186 #ifdef HAVE_PARTITION
187 if (DB_IS_PARTITIONED(dbp))
188 ret = __part_truncate(dbc, countp);
189 else
190 #endif
191 switch (dbp->type) {
192 case DB_BTREE:
193 case DB_RECNO:
194 ret = __bam_truncate(dbc, countp);
195 break;
196 case DB_HASH:
197 ret = __ham_truncate(dbc, countp);
198 break;
199 case DB_HEAP:
200 ret = __heap_truncate(dbc, countp);
201 break;
202 case DB_QUEUE:
203 ret = __qam_truncate(dbc, countp);
204 break;
205 case DB_UNKNOWN:
206 default:
207 ret = __db_unknown_type(env, "DB->truncate", dbp->type);
208 break;
209 }
210
211 /* Discard the cursor. */
212 if (dbc != NULL && (t_ret = __dbc_close(dbc)) != 0 && ret == 0)
213 ret = t_ret;
214
215 /* Delete all blob/external files if that feature is supported. */
216 if (ret == 0 && dbp->blob_threshold)
217 ret = __blob_del_all(dbp, txn, 1);
218
219 DB_TEST_RECOVERY(dbp, DB_TEST_POSTDESTROY, ret, NULL);
220
221 DB_TEST_RECOVERY_LABEL
222
223 return (ret);
224 }
225
226 static int
__db_cursor_check_func(dbc,my_dbc,foundp,pgno,indx,args)227 __db_cursor_check_func(dbc, my_dbc, foundp, pgno, indx, args)
228 DBC *dbc, *my_dbc;
229 u_int32_t *foundp;
230 db_pgno_t pgno;
231 u_int32_t indx;
232 void *args;
233 {
234 COMPQUIET(my_dbc, NULL);
235 COMPQUIET(args, NULL);
236 COMPQUIET(pgno, 0);
237 COMPQUIET(indx, 0);
238 if (IS_INITIALIZED(dbc)) {
239 *foundp = 1;
240 return (EEXIST);
241 }
242 return (0);
243 }
244 /*
245 * __db_cursor_check --
246 * See if there are any active cursors on this db.
247 */
248 static int
__db_cursor_check(dbp)249 __db_cursor_check(dbp)
250 DB *dbp;
251 {
252 int ret;
253 u_int32_t found;
254
255 ret = __db_walk_cursors(dbp, NULL,
256 __db_cursor_check_func, &found, 0, 0, NULL);
257 return (ret == EEXIST ? EINVAL : ret);
258 }
259