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