1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *	Sleepycat Software.  All rights reserved.
6  */
7 
8 #include "config.h"
9 
10 #ifndef lint
11 static const char sccsid[] = "@(#)bt_curadj.c	10.69 (Sleepycat) 12/2/98";
12 #endif /* not lint */
13 
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16 
17 #include <stdlib.h>
18 #endif
19 
20 #include "db_int.h"
21 #include "db_page.h"
22 #include "btree.h"
23 
24 #ifdef DEBUG
25 /*
26  * __bam_cprint --
27  *	Display the current cursor list.
28  *
29  * PUBLIC: int __bam_cprint __P((DB *));
30  */
31 int
__bam_cprint(dbp)32 __bam_cprint(dbp)
33 	DB *dbp;
34 {
35 	CURSOR *cp;
36 	DBC *dbc;
37 
38 	DB_THREAD_LOCK(dbp);
39 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
40 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
41 		cp = (CURSOR *)dbc->internal;
42 		fprintf(stderr,
43 	    "%#0x->%#0x: page: %lu index: %lu dpage %lu dindex: %lu recno: %lu",
44 		    (u_int)dbc, (u_int)cp, (u_long)cp->pgno, (u_long)cp->indx,
45 		    (u_long)cp->dpgno, (u_long)cp->dindx, (u_long)cp->recno);
46 		if (F_ISSET(cp, C_DELETED))
47 			fprintf(stderr, " (deleted)");
48 		fprintf(stderr, "\n");
49 	}
50 	DB_THREAD_UNLOCK(dbp);
51 
52 	return (0);
53 }
54 #endif /* DEBUG */
55 
56 /*
57  * __bam_ca_delete --
58  *	Update the cursors when items are deleted and when already deleted
59  *	items are overwritten.  Return the number of relevant cursors found.
60  *
61  * PUBLIC: int __bam_ca_delete __P((DB *, db_pgno_t, u_int32_t, int));
62  */
63 int
__bam_ca_delete(dbp,pgno,indx,delete)64 __bam_ca_delete(dbp, pgno, indx, delete)
65 	DB *dbp;
66 	db_pgno_t pgno;
67 	u_int32_t indx;
68 	int delete;
69 {
70 	DBC *dbc;
71 	CURSOR *cp;
72 	int count;		/* !!!: Has to contain max number of cursors. */
73 
74 	/* Recno is responsible for its own adjustments. */
75 	if (dbp->type == DB_RECNO)
76 		return (0);
77 
78 	/*
79 	 * Adjust the cursors.  We don't have to review the cursors for any
80 	 * thread of control other than the current one, because we have the
81 	 * page write locked at this point, and any other thread of control
82 	 * had better be using a different locker ID, meaning only cursors in
83 	 * our thread of control can be on the page.
84 	 *
85 	 * It's possible for multiple cursors within the thread to have write
86 	 * locks on the same page, but, cursors within a thread must be single
87 	 * threaded, so all we're locking here is the cursor linked list.
88 	 */
89 	DB_THREAD_LOCK(dbp);
90 	for (count = 0, dbc = TAILQ_FIRST(&dbp->active_queue);
91 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
92 		cp = (CURSOR *)dbc->internal;
93 
94 		if ((cp->pgno == pgno && cp->indx == indx) ||
95 		    (cp->dpgno == pgno && cp->dindx == indx)) {
96 			if (delete)
97 				F_SET(cp, C_DELETED);
98 			else
99 				F_CLR(cp, C_DELETED);
100 			++count;
101 		}
102 	}
103 	DB_THREAD_UNLOCK(dbp);
104 
105 	return (count);
106 }
107 
108 /*
109  * __bam_ca_di --
110  *	Adjust the cursors during a delete or insert.
111  *
112  * PUBLIC: void __bam_ca_di __P((DB *, db_pgno_t, u_int32_t, int));
113  */
114 void
__bam_ca_di(dbp,pgno,indx,adjust)115 __bam_ca_di(dbp, pgno, indx, adjust)
116 	DB *dbp;
117 	db_pgno_t pgno;
118 	u_int32_t indx;
119 	int adjust;
120 {
121 	CURSOR *cp;
122 	DBC *dbc;
123 
124 	/* Recno is responsible for its own adjustments. */
125 	if (dbp->type == DB_RECNO)
126 		return;
127 
128 	/*
129 	 * Adjust the cursors.  See the comment in __bam_ca_delete().
130 	 */
131 	DB_THREAD_LOCK(dbp);
132 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
133 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
134 		cp = (CURSOR *)dbc->internal;
135 		if (cp->pgno == pgno && cp->indx >= indx)
136 			cp->indx += adjust;
137 		if (cp->dpgno == pgno && cp->dindx >= indx)
138 			cp->dindx += adjust;
139 	}
140 	DB_THREAD_UNLOCK(dbp);
141 }
142 
143 /*
144  * __bam_ca_dup --
145  *	Adjust the cursors when moving items from a leaf page to a duplicates
146  *	page.
147  *
148  * PUBLIC: void __bam_ca_dup __P((DB *,
149  * PUBLIC:    db_pgno_t, u_int32_t, u_int32_t, db_pgno_t, u_int32_t));
150  */
151 void
__bam_ca_dup(dbp,fpgno,first,fi,tpgno,ti)152 __bam_ca_dup(dbp, fpgno, first, fi, tpgno, ti)
153 	DB *dbp;
154 	db_pgno_t fpgno, tpgno;
155 	u_int32_t first, fi, ti;
156 {
157 	CURSOR *cp;
158 	DBC *dbc;
159 
160 	/* Recno is responsible for its own adjustments. */
161 	if (dbp->type == DB_RECNO)
162 		return;
163 
164 	/*
165 	 * Adjust the cursors.  See the comment in __bam_ca_delete().
166 	 */
167 	DB_THREAD_LOCK(dbp);
168 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
169 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
170 		cp = (CURSOR *)dbc->internal;
171 		/*
172 		 * Ignore matching entries that have already been moved,
173 		 * we move from the same location on the leaf page more
174 		 * than once.
175 		 */
176 		if (cp->dpgno == PGNO_INVALID &&
177 		    cp->pgno == fpgno && cp->indx == fi) {
178 			cp->indx = first;
179 			cp->dpgno = tpgno;
180 			cp->dindx = ti;
181 		}
182 	}
183 	DB_THREAD_UNLOCK(dbp);
184 }
185 
186 /*
187  * __bam_ca_rsplit --
188  *	Adjust the cursors when doing reverse splits.
189  *
190  * PUBLIC: void __bam_ca_rsplit __P((DB *, db_pgno_t, db_pgno_t));
191  */
192 void
__bam_ca_rsplit(dbp,fpgno,tpgno)193 __bam_ca_rsplit(dbp, fpgno, tpgno)
194 	DB *dbp;
195 	db_pgno_t fpgno, tpgno;
196 {
197 	CURSOR *cp;
198 	DBC *dbc;
199 
200 	/* Recno is responsible for its own adjustments. */
201 	if (dbp->type == DB_RECNO)
202 		return;
203 
204 	/*
205 	 * Adjust the cursors.  See the comment in __bam_ca_delete().
206 	 */
207 	DB_THREAD_LOCK(dbp);
208 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
209 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
210 		cp = (CURSOR *)dbc->internal;
211 		if (cp->pgno == fpgno)
212 			cp->pgno = tpgno;
213 	}
214 	DB_THREAD_UNLOCK(dbp);
215 }
216 
217 /*
218  * __bam_ca_split --
219  *	Adjust the cursors when splitting a page.
220  *
221  * PUBLIC: void __bam_ca_split __P((DB *,
222  * PUBLIC:    db_pgno_t, db_pgno_t, db_pgno_t, u_int32_t, int));
223  */
224 void
__bam_ca_split(dbp,ppgno,lpgno,rpgno,split_indx,cleft)225 __bam_ca_split(dbp, ppgno, lpgno, rpgno, split_indx, cleft)
226 	DB *dbp;
227 	db_pgno_t ppgno, lpgno, rpgno;
228 	u_int32_t split_indx;
229 	int cleft;
230 {
231 	DBC *dbc;
232 	CURSOR *cp;
233 
234 	/* Recno is responsible for its own adjustments. */
235 	if (dbp->type == DB_RECNO)
236 		return;
237 
238 	/*
239 	 * Adjust the cursors.  See the comment in __bam_ca_delete().
240 	 *
241 	 * If splitting the page that a cursor was on, the cursor has to be
242 	 * adjusted to point to the same record as before the split.  Most
243 	 * of the time we don't adjust pointers to the left page, because
244 	 * we're going to copy its contents back over the original page.  If
245 	 * the cursor is on the right page, it is decremented by the number of
246 	 * records split to the left page.
247 	 */
248 	DB_THREAD_LOCK(dbp);
249 	for (dbc = TAILQ_FIRST(&dbp->active_queue);
250 	    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
251 		cp = (CURSOR *)dbc->internal;
252 		if (cp->pgno == ppgno)
253 			if (cp->indx < split_indx) {
254 				if (cleft)
255 					cp->pgno = lpgno;
256 			} else {
257 				cp->pgno = rpgno;
258 				cp->indx -= split_indx;
259 			}
260 		if (cp->dpgno == ppgno)
261 			if (cp->dindx < split_indx) {
262 				if (cleft)
263 					cp->dpgno = lpgno;
264 			} else {
265 				cp->dpgno = rpgno;
266 				cp->dindx -= split_indx;
267 			}
268 	}
269 	DB_THREAD_UNLOCK(dbp);
270 }
271