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
13static 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 */
33int
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 */
65int
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 */
116void
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 */
153void
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 */
194void
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 */
226void
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