17c478bd9Sstevel@tonic-gate /*
256a424ccSmp  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * Copyright (C) 2002 by the Massachusetts Institute of Technology.
87c478bd9Sstevel@tonic-gate  * All rights reserved.
97c478bd9Sstevel@tonic-gate  *
107c478bd9Sstevel@tonic-gate  * Export of this software from the United States of America may
117c478bd9Sstevel@tonic-gate  *   require a specific license from the United States Government.
127c478bd9Sstevel@tonic-gate  *   It is the responsibility of any person or organization contemplating
137c478bd9Sstevel@tonic-gate  *   export to obtain such a license before exporting.
14*1da57d55SToomas Soome  *
157c478bd9Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
167c478bd9Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
177c478bd9Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
187c478bd9Sstevel@tonic-gate  * notice appear in all copies and that both that copyright notice and
197c478bd9Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
207c478bd9Sstevel@tonic-gate  * the name of M.I.T. not be used in advertising or publicity pertaining
217c478bd9Sstevel@tonic-gate  * to distribution of the software without specific, written prior
227c478bd9Sstevel@tonic-gate  * permission.  Furthermore if you modify this software you must label
237c478bd9Sstevel@tonic-gate  * your software as modified software and not distribute it in such a
247c478bd9Sstevel@tonic-gate  * fashion that it might be confused with the original M.I.T. software.
257c478bd9Sstevel@tonic-gate  * M.I.T. makes no representations about the suitability of
267c478bd9Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
277c478bd9Sstevel@tonic-gate  * or implied warranty.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*-
317c478bd9Sstevel@tonic-gate  * Copyright (c) 1990, 1993, 1994
327c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * This code is derived from software contributed to Berkeley by
357c478bd9Sstevel@tonic-gate  * Mike Olson.
367c478bd9Sstevel@tonic-gate  *
377c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
387c478bd9Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
397c478bd9Sstevel@tonic-gate  * are met:
407c478bd9Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
417c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
427c478bd9Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
437c478bd9Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
447c478bd9Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
457c478bd9Sstevel@tonic-gate  * 3. All advertising materials mentioning features or use of this software
467c478bd9Sstevel@tonic-gate  *    must display the following acknowledgement:
477c478bd9Sstevel@tonic-gate  *	This product includes software developed by the University of
487c478bd9Sstevel@tonic-gate  *	California, Berkeley and its contributors.
497c478bd9Sstevel@tonic-gate  * 4. Neither the name of the University nor the names of its contributors
507c478bd9Sstevel@tonic-gate  *    may be used to endorse or promote products derived from this software
517c478bd9Sstevel@tonic-gate  *    without specific prior written permission.
527c478bd9Sstevel@tonic-gate  *
537c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
547c478bd9Sstevel@tonic-gate  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
557c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
567c478bd9Sstevel@tonic-gate  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
577c478bd9Sstevel@tonic-gate  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
587c478bd9Sstevel@tonic-gate  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
597c478bd9Sstevel@tonic-gate  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
607c478bd9Sstevel@tonic-gate  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
617c478bd9Sstevel@tonic-gate  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
627c478bd9Sstevel@tonic-gate  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
637c478bd9Sstevel@tonic-gate  * SUCH DAMAGE.
647c478bd9Sstevel@tonic-gate  */
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate #if defined(LIBC_SCCS) && !defined(lint)
677c478bd9Sstevel@tonic-gate static char sccsid[] = "@(#)bt_seq.c	8.9 (Berkeley) 6/20/95";
687c478bd9Sstevel@tonic-gate #endif /* LIBC_SCCS and not lint */
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate #include <sys/types.h>
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #include <errno.h>
737c478bd9Sstevel@tonic-gate #include <stddef.h>
747c478bd9Sstevel@tonic-gate #include <stdio.h>
757c478bd9Sstevel@tonic-gate #include <stdlib.h>
7656a424ccSmp #include <string.h>
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate #include "db-int.h"
797c478bd9Sstevel@tonic-gate #include "btree.h"
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate static int __bt_first __P((BTREE *, const DBT *, EPG *, int *));
827c478bd9Sstevel@tonic-gate static int __bt_seqadv __P((BTREE *, EPG *, int));
837c478bd9Sstevel@tonic-gate static int __bt_seqset __P((BTREE *, EPG *, DBT *, int));
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /*
867c478bd9Sstevel@tonic-gate  * Sequential scan support.
877c478bd9Sstevel@tonic-gate  *
887c478bd9Sstevel@tonic-gate  * The tree can be scanned sequentially, starting from either end of the
897c478bd9Sstevel@tonic-gate  * tree or from any specific key.  A scan request before any scanning is
907c478bd9Sstevel@tonic-gate  * done is initialized as starting from the least node.
917c478bd9Sstevel@tonic-gate  */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * __bt_seq --
957c478bd9Sstevel@tonic-gate  *	Btree sequential scan interface.
967c478bd9Sstevel@tonic-gate  *
977c478bd9Sstevel@tonic-gate  * Parameters:
987c478bd9Sstevel@tonic-gate  *	dbp:	pointer to access method
997c478bd9Sstevel@tonic-gate  *	key:	key for positioning and return value
1007c478bd9Sstevel@tonic-gate  *	data:	data return value
1017c478bd9Sstevel@tonic-gate  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
1027c478bd9Sstevel@tonic-gate  *
1037c478bd9Sstevel@tonic-gate  * Returns:
1047c478bd9Sstevel@tonic-gate  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
1057c478bd9Sstevel@tonic-gate  */
1067c478bd9Sstevel@tonic-gate int
__bt_seq(dbp,key,data,flags)1077c478bd9Sstevel@tonic-gate __bt_seq(dbp, key, data, flags)
1087c478bd9Sstevel@tonic-gate 	const DB *dbp;
1097c478bd9Sstevel@tonic-gate 	DBT *key, *data;
1107c478bd9Sstevel@tonic-gate 	u_int flags;
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate 	BTREE *t;
1137c478bd9Sstevel@tonic-gate 	EPG e;
1147c478bd9Sstevel@tonic-gate 	int status;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	t = dbp->internal;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	/* Toss any page pinned across calls. */
1197c478bd9Sstevel@tonic-gate 	if (t->bt_pinned != NULL) {
1207c478bd9Sstevel@tonic-gate 		mpool_put(t->bt_mp, t->bt_pinned, 0);
1217c478bd9Sstevel@tonic-gate 		t->bt_pinned = NULL;
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	/*
1257c478bd9Sstevel@tonic-gate 	 * If scan unitialized as yet, or starting at a specific record, set
1267c478bd9Sstevel@tonic-gate 	 * the scan to a specific key.  Both __bt_seqset and __bt_seqadv pin
1277c478bd9Sstevel@tonic-gate 	 * the page the cursor references if they're successful.
1287c478bd9Sstevel@tonic-gate 	 */
1297c478bd9Sstevel@tonic-gate 	switch (flags) {
1307c478bd9Sstevel@tonic-gate 	case R_NEXT:
1317c478bd9Sstevel@tonic-gate 	case R_PREV:
1327c478bd9Sstevel@tonic-gate 		if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
1337c478bd9Sstevel@tonic-gate 			status = __bt_seqadv(t, &e, flags);
1347c478bd9Sstevel@tonic-gate 			break;
1357c478bd9Sstevel@tonic-gate 		}
1367c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1377c478bd9Sstevel@tonic-gate 	case R_FIRST:
1387c478bd9Sstevel@tonic-gate 	case R_LAST:
1397c478bd9Sstevel@tonic-gate 	case R_CURSOR:
1407c478bd9Sstevel@tonic-gate 		status = __bt_seqset(t, &e, key, flags);
1417c478bd9Sstevel@tonic-gate 		break;
1427c478bd9Sstevel@tonic-gate 	default:
1437c478bd9Sstevel@tonic-gate 		errno = EINVAL;
1447c478bd9Sstevel@tonic-gate 		return (RET_ERROR);
1457c478bd9Sstevel@tonic-gate 	}
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	if (status == RET_SUCCESS) {
1487c478bd9Sstevel@tonic-gate 		__bt_setcur(t, e.page->pgno, e.index);
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 		status =
1517c478bd9Sstevel@tonic-gate 		    __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0);
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 		/*
1547c478bd9Sstevel@tonic-gate 		 * If the user is doing concurrent access, we copied the
1557c478bd9Sstevel@tonic-gate 		 * key/data, toss the page.
1567c478bd9Sstevel@tonic-gate 		 */
1577c478bd9Sstevel@tonic-gate 		if (F_ISSET(t, B_DB_LOCK))
1587c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, e.page, 0);
1597c478bd9Sstevel@tonic-gate 		else
1607c478bd9Sstevel@tonic-gate 			t->bt_pinned = e.page;
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 	return (status);
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate /*
1667c478bd9Sstevel@tonic-gate  * __bt_seqset --
1677c478bd9Sstevel@tonic-gate  *	Set the sequential scan to a specific key.
1687c478bd9Sstevel@tonic-gate  *
1697c478bd9Sstevel@tonic-gate  * Parameters:
1707c478bd9Sstevel@tonic-gate  *	t:	tree
1717c478bd9Sstevel@tonic-gate  *	ep:	storage for returned key
1727c478bd9Sstevel@tonic-gate  *	key:	key for initial scan position
1737c478bd9Sstevel@tonic-gate  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
1747c478bd9Sstevel@tonic-gate  *
1757c478bd9Sstevel@tonic-gate  * Side effects:
1767c478bd9Sstevel@tonic-gate  *	Pins the page the cursor references.
1777c478bd9Sstevel@tonic-gate  *
1787c478bd9Sstevel@tonic-gate  * Returns:
1797c478bd9Sstevel@tonic-gate  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
1807c478bd9Sstevel@tonic-gate  */
1817c478bd9Sstevel@tonic-gate static int
__bt_seqset(t,ep,key,flags)1827c478bd9Sstevel@tonic-gate __bt_seqset(t, ep, key, flags)
1837c478bd9Sstevel@tonic-gate 	BTREE *t;
1847c478bd9Sstevel@tonic-gate 	EPG *ep;
1857c478bd9Sstevel@tonic-gate 	DBT *key;
1867c478bd9Sstevel@tonic-gate 	int flags;
1877c478bd9Sstevel@tonic-gate {
1887c478bd9Sstevel@tonic-gate 	PAGE *h;
1897c478bd9Sstevel@tonic-gate 	db_pgno_t pg;
1907c478bd9Sstevel@tonic-gate 	int exact;
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/*
1937c478bd9Sstevel@tonic-gate 	 * Find the first, last or specific key in the tree and point the
1947c478bd9Sstevel@tonic-gate 	 * cursor at it.  The cursor may not be moved until a new key has
1957c478bd9Sstevel@tonic-gate 	 * been found.
1967c478bd9Sstevel@tonic-gate 	 */
1977c478bd9Sstevel@tonic-gate 	switch (flags) {
1987c478bd9Sstevel@tonic-gate 	case R_CURSOR:				/* Keyed scan. */
1997c478bd9Sstevel@tonic-gate 		/*
2007c478bd9Sstevel@tonic-gate 		 * Find the first instance of the key or the smallest key
2017c478bd9Sstevel@tonic-gate 		 * which is greater than or equal to the specified key.
2027c478bd9Sstevel@tonic-gate 		 */
2037c478bd9Sstevel@tonic-gate 		if (key->data == NULL || key->size == 0) {
2047c478bd9Sstevel@tonic-gate 			errno = EINVAL;
2057c478bd9Sstevel@tonic-gate 			return (RET_ERROR);
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 		return (__bt_first(t, key, ep, &exact));
2087c478bd9Sstevel@tonic-gate 	case R_FIRST:				/* First record. */
2097c478bd9Sstevel@tonic-gate 	case R_NEXT:
2107c478bd9Sstevel@tonic-gate 		/* Walk down the left-hand side of the tree. */
2117c478bd9Sstevel@tonic-gate 		for (pg = P_ROOT;;) {
2127c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
2137c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate 			/* Check for an empty tree. */
2167c478bd9Sstevel@tonic-gate 			if (NEXTINDEX(h) == 0) {
2177c478bd9Sstevel@tonic-gate 				mpool_put(t->bt_mp, h, 0);
2187c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
2197c478bd9Sstevel@tonic-gate 			}
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 			if (h->flags & (P_BLEAF | P_RLEAF))
2227c478bd9Sstevel@tonic-gate 				break;
2237c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, 0)->pgno;
2247c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
2257c478bd9Sstevel@tonic-gate 		}
2267c478bd9Sstevel@tonic-gate 		ep->page = h;
2277c478bd9Sstevel@tonic-gate 		ep->index = 0;
2287c478bd9Sstevel@tonic-gate 		break;
2297c478bd9Sstevel@tonic-gate 	case R_LAST:				/* Last record. */
2307c478bd9Sstevel@tonic-gate 	case R_PREV:
2317c478bd9Sstevel@tonic-gate 		/* Walk down the right-hand side of the tree. */
2327c478bd9Sstevel@tonic-gate 		for (pg = P_ROOT;;) {
2337c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
2347c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 			/* Check for an empty tree. */
2377c478bd9Sstevel@tonic-gate 			if (NEXTINDEX(h) == 0) {
2387c478bd9Sstevel@tonic-gate 				mpool_put(t->bt_mp, h, 0);
2397c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
2407c478bd9Sstevel@tonic-gate 			}
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 			if (h->flags & (P_BLEAF | P_RLEAF))
2437c478bd9Sstevel@tonic-gate 				break;
2447c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
2457c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
2467c478bd9Sstevel@tonic-gate 		}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		ep->page = h;
2497c478bd9Sstevel@tonic-gate 		ep->index = NEXTINDEX(h) - 1;
2507c478bd9Sstevel@tonic-gate 		break;
2517c478bd9Sstevel@tonic-gate 	}
2527c478bd9Sstevel@tonic-gate 	return (RET_SUCCESS);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate  * __bt_seqadvance --
2577c478bd9Sstevel@tonic-gate  *	Advance the sequential scan.
2587c478bd9Sstevel@tonic-gate  *
2597c478bd9Sstevel@tonic-gate  * Parameters:
2607c478bd9Sstevel@tonic-gate  *	t:	tree
2617c478bd9Sstevel@tonic-gate  *	flags:	R_NEXT, R_PREV
2627c478bd9Sstevel@tonic-gate  *
2637c478bd9Sstevel@tonic-gate  * Side effects:
2647c478bd9Sstevel@tonic-gate  *	Pins the page the new key/data record is on.
2657c478bd9Sstevel@tonic-gate  *
2667c478bd9Sstevel@tonic-gate  * Returns:
2677c478bd9Sstevel@tonic-gate  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
2687c478bd9Sstevel@tonic-gate  */
2697c478bd9Sstevel@tonic-gate static int
__bt_seqadv(t,ep,flags)2707c478bd9Sstevel@tonic-gate __bt_seqadv(t, ep, flags)
2717c478bd9Sstevel@tonic-gate 	BTREE *t;
2727c478bd9Sstevel@tonic-gate 	EPG *ep;
2737c478bd9Sstevel@tonic-gate 	int flags;
2747c478bd9Sstevel@tonic-gate {
2757c478bd9Sstevel@tonic-gate 	CURSOR *c;
2767c478bd9Sstevel@tonic-gate 	PAGE *h;
27756a424ccSmp 	indx_t idx;
2787c478bd9Sstevel@tonic-gate 	db_pgno_t pg;
2797c478bd9Sstevel@tonic-gate 	int exact, rval;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	/*
2827c478bd9Sstevel@tonic-gate 	 * There are a couple of states that we can be in.  The cursor has
2837c478bd9Sstevel@tonic-gate 	 * been initialized by the time we get here, but that's all we know.
2847c478bd9Sstevel@tonic-gate 	 */
2857c478bd9Sstevel@tonic-gate 	c = &t->bt_cursor;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	/*
2887c478bd9Sstevel@tonic-gate 	 * The cursor was deleted and there weren't any duplicate records,
2897c478bd9Sstevel@tonic-gate 	 * so the cursor's key was saved.  Find out where that key would
2907c478bd9Sstevel@tonic-gate 	 * be in the current tree.  If the returned key is an exact match,
2917c478bd9Sstevel@tonic-gate 	 * it means that a key/data pair was inserted into the tree after
2927c478bd9Sstevel@tonic-gate 	 * the delete.  We could reasonably return the key, but the problem
2937c478bd9Sstevel@tonic-gate 	 * is that this is the access pattern we'll see if the user is
2947c478bd9Sstevel@tonic-gate 	 * doing seq(..., R_NEXT)/put(..., 0) pairs, i.e. the put deletes
2957c478bd9Sstevel@tonic-gate 	 * the cursor record and then replaces it, so the cursor was saved,
2967c478bd9Sstevel@tonic-gate 	 * and we'll simply return the same "new" record until the user
2977c478bd9Sstevel@tonic-gate 	 * notices and doesn't do a put() of it.  Since the key is an exact
2987c478bd9Sstevel@tonic-gate 	 * match, we could as easily put the new record before the cursor,
2997c478bd9Sstevel@tonic-gate 	 * and we've made no guarantee to return it.  So, move forward or
3007c478bd9Sstevel@tonic-gate 	 * back a record if it's an exact match.
3017c478bd9Sstevel@tonic-gate 	 *
3027c478bd9Sstevel@tonic-gate 	 * XXX
3037c478bd9Sstevel@tonic-gate 	 * In the current implementation, put's to the cursor are done with
3047c478bd9Sstevel@tonic-gate 	 * delete/add pairs.  This has two consequences.  First, it means
3057c478bd9Sstevel@tonic-gate 	 * that seq(..., R_NEXT)/put(..., R_CURSOR) pairs are going to exhibit
3067c478bd9Sstevel@tonic-gate 	 * the same behavior as above.  Second, you can return the same key
3077c478bd9Sstevel@tonic-gate 	 * twice if you have duplicate records.  The scenario is that the
3087c478bd9Sstevel@tonic-gate 	 * cursor record is deleted, moving the cursor forward or backward
3097c478bd9Sstevel@tonic-gate 	 * to a duplicate.  The add then inserts the new record at a location
3107c478bd9Sstevel@tonic-gate 	 * ahead of the cursor because duplicates aren't sorted in any way,
3117c478bd9Sstevel@tonic-gate 	 * and the new record is later returned.  This has to be fixed at some
3127c478bd9Sstevel@tonic-gate 	 * point.
3137c478bd9Sstevel@tonic-gate 	 */
3147c478bd9Sstevel@tonic-gate 	if (F_ISSET(c, CURS_ACQUIRE)) {
3157c478bd9Sstevel@tonic-gate 		if ((rval = __bt_first(t, &c->key, ep, &exact)) == RET_ERROR)
3167c478bd9Sstevel@tonic-gate 			return (RET_ERROR);
3177c478bd9Sstevel@tonic-gate 		if (!exact)
3187c478bd9Sstevel@tonic-gate 			return (rval);
3197c478bd9Sstevel@tonic-gate 		/*
3207c478bd9Sstevel@tonic-gate 		 * XXX
3217c478bd9Sstevel@tonic-gate 		 * Kluge -- get, release, get the page.
3227c478bd9Sstevel@tonic-gate 		 */
3237c478bd9Sstevel@tonic-gate 		c->pg.pgno = ep->page->pgno;
3247c478bd9Sstevel@tonic-gate 		c->pg.index = ep->index;
3257c478bd9Sstevel@tonic-gate 		mpool_put(t->bt_mp, ep->page, 0);
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	/* Get the page referenced by the cursor. */
3297c478bd9Sstevel@tonic-gate 	if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
3307c478bd9Sstevel@tonic-gate 		return (RET_ERROR);
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/*
3337c478bd9Sstevel@tonic-gate  	 * Find the next/previous record in the tree and point the cursor at
3347c478bd9Sstevel@tonic-gate 	 * it.  The cursor may not be moved until a new key has been found.
3357c478bd9Sstevel@tonic-gate 	 */
3367c478bd9Sstevel@tonic-gate 	switch (flags) {
3377c478bd9Sstevel@tonic-gate 	case R_NEXT:			/* Next record. */
3387c478bd9Sstevel@tonic-gate 		/*
3397c478bd9Sstevel@tonic-gate 		 * The cursor was deleted in duplicate records, and moved
3407c478bd9Sstevel@tonic-gate 		 * forward to a record that has yet to be returned.  Clear
3417c478bd9Sstevel@tonic-gate 		 * that flag, and return the record.
3427c478bd9Sstevel@tonic-gate 		 */
3437c478bd9Sstevel@tonic-gate 		if (F_ISSET(c, CURS_AFTER))
3447c478bd9Sstevel@tonic-gate 			goto usecurrent;
34556a424ccSmp 		idx = c->pg.index;
34656a424ccSmp 		if (++idx == NEXTINDEX(h)) {
3477c478bd9Sstevel@tonic-gate 			pg = h->nextpg;
3487c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
3497c478bd9Sstevel@tonic-gate 			if (pg == P_INVALID)
3507c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
3517c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
3527c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
35356a424ccSmp 			idx = 0;
3547c478bd9Sstevel@tonic-gate 		}
3557c478bd9Sstevel@tonic-gate 		break;
3567c478bd9Sstevel@tonic-gate 	case R_PREV:			/* Previous record. */
3577c478bd9Sstevel@tonic-gate 		/*
3587c478bd9Sstevel@tonic-gate 		 * The cursor was deleted in duplicate records, and moved
3597c478bd9Sstevel@tonic-gate 		 * backward to a record that has yet to be returned.  Clear
3607c478bd9Sstevel@tonic-gate 		 * that flag, and return the record.
3617c478bd9Sstevel@tonic-gate 		 */
3627c478bd9Sstevel@tonic-gate 		if (F_ISSET(c, CURS_BEFORE)) {
3637c478bd9Sstevel@tonic-gate usecurrent:		F_CLR(c, CURS_AFTER | CURS_BEFORE);
3647c478bd9Sstevel@tonic-gate 			ep->page = h;
3657c478bd9Sstevel@tonic-gate 			ep->index = c->pg.index;
3667c478bd9Sstevel@tonic-gate 			return (RET_SUCCESS);
3677c478bd9Sstevel@tonic-gate 		}
36856a424ccSmp 		idx = c->pg.index;
36956a424ccSmp 		if (idx == 0) {
3707c478bd9Sstevel@tonic-gate 			pg = h->prevpg;
3717c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
3727c478bd9Sstevel@tonic-gate 			if (pg == P_INVALID)
3737c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
3747c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
3757c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
37656a424ccSmp 			idx = NEXTINDEX(h) - 1;
3777c478bd9Sstevel@tonic-gate 		} else
37856a424ccSmp 			--idx;
3797c478bd9Sstevel@tonic-gate 		break;
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	ep->page = h;
38356a424ccSmp 	ep->index = idx;
3847c478bd9Sstevel@tonic-gate 	return (RET_SUCCESS);
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate /*
3887c478bd9Sstevel@tonic-gate  * __bt_first --
3897c478bd9Sstevel@tonic-gate  *	Find the first entry.
3907c478bd9Sstevel@tonic-gate  *
3917c478bd9Sstevel@tonic-gate  * Parameters:
3927c478bd9Sstevel@tonic-gate  *	t:	the tree
3937c478bd9Sstevel@tonic-gate  *    key:	the key
3947c478bd9Sstevel@tonic-gate  *  erval:	return EPG
3957c478bd9Sstevel@tonic-gate  * exactp:	pointer to exact match flag
3967c478bd9Sstevel@tonic-gate  *
3977c478bd9Sstevel@tonic-gate  * Returns:
3987c478bd9Sstevel@tonic-gate  *	The first entry in the tree greater than or equal to key,
3997c478bd9Sstevel@tonic-gate  *	or RET_SPECIAL if no such key exists.
4007c478bd9Sstevel@tonic-gate  */
4017c478bd9Sstevel@tonic-gate static int
__bt_first(t,key,erval,exactp)4027c478bd9Sstevel@tonic-gate __bt_first(t, key, erval, exactp)
4037c478bd9Sstevel@tonic-gate 	BTREE *t;
4047c478bd9Sstevel@tonic-gate 	const DBT *key;
4057c478bd9Sstevel@tonic-gate 	EPG *erval;
4067c478bd9Sstevel@tonic-gate 	int *exactp;
4077c478bd9Sstevel@tonic-gate {
4087c478bd9Sstevel@tonic-gate 	PAGE *h;
4097c478bd9Sstevel@tonic-gate 	EPG *ep, save;
4107c478bd9Sstevel@tonic-gate 	db_pgno_t pg;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	/*
4137c478bd9Sstevel@tonic-gate 	 * Find any matching record; __bt_search pins the page.
4147c478bd9Sstevel@tonic-gate 	 *
4157c478bd9Sstevel@tonic-gate 	 * If it's an exact match and duplicates are possible, walk backwards
4167c478bd9Sstevel@tonic-gate 	 * in the tree until we find the first one.  Otherwise, make sure it's
4177c478bd9Sstevel@tonic-gate 	 * a valid key (__bt_search may return an index just past the end of a
4187c478bd9Sstevel@tonic-gate 	 * page) and return it.
4197c478bd9Sstevel@tonic-gate 	 */
4207c478bd9Sstevel@tonic-gate 	if ((ep = __bt_search(t, key, exactp)) == NULL)
4217c478bd9Sstevel@tonic-gate 		return (RET_SPECIAL);
4227c478bd9Sstevel@tonic-gate 	if (*exactp) {
4237c478bd9Sstevel@tonic-gate 		if (F_ISSET(t, B_NODUPS)) {
4247c478bd9Sstevel@tonic-gate 			*erval = *ep;
4257c478bd9Sstevel@tonic-gate 			return (RET_SUCCESS);
4267c478bd9Sstevel@tonic-gate 		}
427*1da57d55SToomas Soome 
4287c478bd9Sstevel@tonic-gate 		/*
4297c478bd9Sstevel@tonic-gate 		 * Walk backwards, as long as the entry matches and there are
4307c478bd9Sstevel@tonic-gate 		 * keys left in the tree.  Save a copy of each match in case
4317c478bd9Sstevel@tonic-gate 		 * we go too far.
4327c478bd9Sstevel@tonic-gate 		 */
4337c478bd9Sstevel@tonic-gate 		save = *ep;
4347c478bd9Sstevel@tonic-gate 		h = ep->page;
4357c478bd9Sstevel@tonic-gate 		do {
4367c478bd9Sstevel@tonic-gate 			if (save.page->pgno != ep->page->pgno) {
4377c478bd9Sstevel@tonic-gate 				mpool_put(t->bt_mp, save.page, 0);
4387c478bd9Sstevel@tonic-gate 				save = *ep;
4397c478bd9Sstevel@tonic-gate 			} else
4407c478bd9Sstevel@tonic-gate 				save.index = ep->index;
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 			/*
4437c478bd9Sstevel@tonic-gate 			 * Don't unpin the page the last (or original) match
4447c478bd9Sstevel@tonic-gate 			 * was on, but make sure it's unpinned if an error
4457c478bd9Sstevel@tonic-gate 			 * occurs.
4467c478bd9Sstevel@tonic-gate 			 */
4477c478bd9Sstevel@tonic-gate 			if (ep->index == 0) {
4487c478bd9Sstevel@tonic-gate 				if (h->prevpg == P_INVALID)
4497c478bd9Sstevel@tonic-gate 					break;
4507c478bd9Sstevel@tonic-gate 				if (h->pgno != save.page->pgno)
4517c478bd9Sstevel@tonic-gate 					mpool_put(t->bt_mp, h, 0);
4527c478bd9Sstevel@tonic-gate 				if ((h = mpool_get(t->bt_mp,
4537c478bd9Sstevel@tonic-gate 				    h->prevpg, 0)) == NULL) {
4547c478bd9Sstevel@tonic-gate 					if (h->pgno == save.page->pgno)
4557c478bd9Sstevel@tonic-gate 						mpool_put(t->bt_mp,
4567c478bd9Sstevel@tonic-gate 						    save.page, 0);
4577c478bd9Sstevel@tonic-gate 					return (RET_ERROR);
4587c478bd9Sstevel@tonic-gate 				}
4597c478bd9Sstevel@tonic-gate 				ep->page = h;
4607c478bd9Sstevel@tonic-gate 				ep->index = NEXTINDEX(h);
4617c478bd9Sstevel@tonic-gate 			}
4627c478bd9Sstevel@tonic-gate 			--ep->index;
4637c478bd9Sstevel@tonic-gate 		} while (__bt_cmp(t, key, ep) == 0);
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 		/*
4667c478bd9Sstevel@tonic-gate 		 * Reach here with the last page that was looked at pinned,
4677c478bd9Sstevel@tonic-gate 		 * which may or may not be the same as the last (or original)
4687c478bd9Sstevel@tonic-gate 		 * match page.  If it's not useful, release it.
4697c478bd9Sstevel@tonic-gate 		 */
4707c478bd9Sstevel@tonic-gate 		if (h->pgno != save.page->pgno)
4717c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 		*erval = save;
4747c478bd9Sstevel@tonic-gate 		return (RET_SUCCESS);
4757c478bd9Sstevel@tonic-gate 	}
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	/* If at the end of a page, find the next entry. */
4787c478bd9Sstevel@tonic-gate 	if (ep->index == NEXTINDEX(ep->page)) {
4797c478bd9Sstevel@tonic-gate 		h = ep->page;
4807c478bd9Sstevel@tonic-gate 		pg = h->nextpg;
4817c478bd9Sstevel@tonic-gate 		mpool_put(t->bt_mp, h, 0);
4827c478bd9Sstevel@tonic-gate 		if (pg == P_INVALID)
4837c478bd9Sstevel@tonic-gate 			return (RET_SPECIAL);
4847c478bd9Sstevel@tonic-gate 		if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
4857c478bd9Sstevel@tonic-gate 			return (RET_ERROR);
4867c478bd9Sstevel@tonic-gate 		ep->index = 0;
4877c478bd9Sstevel@tonic-gate 		ep->page = h;
4887c478bd9Sstevel@tonic-gate 	}
4897c478bd9Sstevel@tonic-gate 	*erval = *ep;
4907c478bd9Sstevel@tonic-gate 	return (RET_SUCCESS);
4917c478bd9Sstevel@tonic-gate }
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate /*
4947c478bd9Sstevel@tonic-gate  * __bt_setcur --
4957c478bd9Sstevel@tonic-gate  *	Set the cursor to an entry in the tree.
4967c478bd9Sstevel@tonic-gate  *
4977c478bd9Sstevel@tonic-gate  * Parameters:
4987c478bd9Sstevel@tonic-gate  *	t:	the tree
4997c478bd9Sstevel@tonic-gate  *   pgno:	page number
5007c478bd9Sstevel@tonic-gate  *  index:	page index
5017c478bd9Sstevel@tonic-gate  */
5027c478bd9Sstevel@tonic-gate void
__bt_setcur(t,pgno,idx)50356a424ccSmp __bt_setcur(t, pgno, idx)
5047c478bd9Sstevel@tonic-gate 	BTREE *t;
5057c478bd9Sstevel@tonic-gate 	db_pgno_t pgno;
50656a424ccSmp 	u_int idx;
5077c478bd9Sstevel@tonic-gate {
5087c478bd9Sstevel@tonic-gate 	/* Lose any already deleted key. */
5097c478bd9Sstevel@tonic-gate 	if (t->bt_cursor.key.data != NULL) {
5107c478bd9Sstevel@tonic-gate 		free(t->bt_cursor.key.data);
5117c478bd9Sstevel@tonic-gate 		t->bt_cursor.key.size = 0;
5127c478bd9Sstevel@tonic-gate 		t->bt_cursor.key.data = NULL;
5137c478bd9Sstevel@tonic-gate 	}
5147c478bd9Sstevel@tonic-gate 	F_CLR(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	/* Update the cursor. */
5177c478bd9Sstevel@tonic-gate 	t->bt_cursor.pg.pgno = pgno;
51856a424ccSmp 	t->bt_cursor.pg.index = idx;
5197c478bd9Sstevel@tonic-gate 	F_SET(&t->bt_cursor, CURS_INIT);
5207c478bd9Sstevel@tonic-gate }
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /* Recursive descent cursor. */
5237c478bd9Sstevel@tonic-gate typedef struct rcursor_ {
5247c478bd9Sstevel@tonic-gate 	CURSOR	cursor;
5257c478bd9Sstevel@tonic-gate 	size_t	ssize;
5267c478bd9Sstevel@tonic-gate 	EPGNO	*stack;
5277c478bd9Sstevel@tonic-gate 	EPGNO	*sp;
5287c478bd9Sstevel@tonic-gate } RCURSOR;
5297c478bd9Sstevel@tonic-gate #define RCURSOR_MINSS	64
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate static int	 bt_rcinit(void **);
5327c478bd9Sstevel@tonic-gate static void	 bt_rcdestroy(void **);
5337c478bd9Sstevel@tonic-gate static int	 bt_rcpush(RCURSOR *, db_pgno_t, u_int);
5347c478bd9Sstevel@tonic-gate static EPGNO	*bt_rcpop(RCURSOR *);
5357c478bd9Sstevel@tonic-gate static void	 bt_rcclr(RCURSOR *);
5367c478bd9Sstevel@tonic-gate static int	 bt_rcgrowstk(RCURSOR *);
5377c478bd9Sstevel@tonic-gate static int	 bt_rseqset(BTREE *, EPG *, DBT *, RCURSOR *, int);
5387c478bd9Sstevel@tonic-gate static int	 bt_rseqadv(BTREE *, EPG *, RCURSOR *, int);
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate static int
bt_rcinit(curs)5417c478bd9Sstevel@tonic-gate bt_rcinit(curs)
5427c478bd9Sstevel@tonic-gate 	void **curs;
5437c478bd9Sstevel@tonic-gate {
5447c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	rc = *curs = malloc(sizeof(RCURSOR));
5477c478bd9Sstevel@tonic-gate 	if (rc == NULL) {
5487c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
5497c478bd9Sstevel@tonic-gate 		return RET_ERROR;
5507c478bd9Sstevel@tonic-gate 	}
5517c478bd9Sstevel@tonic-gate 	memset(rc, 0, sizeof(*rc));
5527c478bd9Sstevel@tonic-gate 
5537c478bd9Sstevel@tonic-gate 	rc->ssize = RCURSOR_MINSS;
5547c478bd9Sstevel@tonic-gate 	rc->stack = malloc(rc->ssize * sizeof(EPGNO));
5557c478bd9Sstevel@tonic-gate 	if (rc->stack == NULL) {
5567c478bd9Sstevel@tonic-gate 		free(rc);
5577c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
5587c478bd9Sstevel@tonic-gate 		return RET_ERROR;
5597c478bd9Sstevel@tonic-gate 	}
5607c478bd9Sstevel@tonic-gate 	bt_rcclr(rc);
5617c478bd9Sstevel@tonic-gate 	return RET_SUCCESS;
5627c478bd9Sstevel@tonic-gate }
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate static void
bt_rcdestroy(curs)5657c478bd9Sstevel@tonic-gate bt_rcdestroy(curs)
5667c478bd9Sstevel@tonic-gate 	void **curs;
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
5697c478bd9Sstevel@tonic-gate 
5707c478bd9Sstevel@tonic-gate 	rc = *curs;
5717c478bd9Sstevel@tonic-gate 	free(rc->stack);
5727c478bd9Sstevel@tonic-gate 	free(rc);
5737c478bd9Sstevel@tonic-gate 	*curs = NULL;
5747c478bd9Sstevel@tonic-gate }
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate static int
bt_rcpush(rc,p,i)5777c478bd9Sstevel@tonic-gate bt_rcpush(rc, p, i)
5787c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
5797c478bd9Sstevel@tonic-gate 	db_pgno_t p;
5807c478bd9Sstevel@tonic-gate 	u_int i;
5817c478bd9Sstevel@tonic-gate {
5827c478bd9Sstevel@tonic-gate 	int status;
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	rc->sp->pgno = p;
5857c478bd9Sstevel@tonic-gate 	rc->sp->index = i;
5867c478bd9Sstevel@tonic-gate 	if (++rc->sp > rc->stack + rc->ssize) {
5877c478bd9Sstevel@tonic-gate 		status = bt_rcgrowstk(rc);
5887c478bd9Sstevel@tonic-gate 		if (status != RET_SUCCESS)
5897c478bd9Sstevel@tonic-gate 			return status;
5907c478bd9Sstevel@tonic-gate 	}
5917c478bd9Sstevel@tonic-gate 	return RET_SUCCESS;
5927c478bd9Sstevel@tonic-gate }
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate static EPGNO *
bt_rcpop(rc)5957c478bd9Sstevel@tonic-gate bt_rcpop(rc)
5967c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
5977c478bd9Sstevel@tonic-gate {
5987c478bd9Sstevel@tonic-gate 	return (rc->sp == rc->stack) ? NULL : --rc->sp;
5997c478bd9Sstevel@tonic-gate }
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate static void
bt_rcclr(rc)6027c478bd9Sstevel@tonic-gate bt_rcclr(rc)
6037c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
6047c478bd9Sstevel@tonic-gate {
6057c478bd9Sstevel@tonic-gate 	rc->sp = rc->stack;
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate static int
bt_rcgrowstk(rc)6097c478bd9Sstevel@tonic-gate bt_rcgrowstk(rc)
6107c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
6117c478bd9Sstevel@tonic-gate {
6127c478bd9Sstevel@tonic-gate 	size_t osize;
6137c478bd9Sstevel@tonic-gate 	EPGNO *e;
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	osize = rc->ssize;
6167c478bd9Sstevel@tonic-gate 	rc->ssize *= 2;
6177c478bd9Sstevel@tonic-gate 	e = realloc(rc->stack, rc->ssize * sizeof(EPGNO));
6187c478bd9Sstevel@tonic-gate 	if (e == NULL) {
6197c478bd9Sstevel@tonic-gate 		rc->ssize = osize;
6207c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
6217c478bd9Sstevel@tonic-gate 		return RET_ERROR;
6227c478bd9Sstevel@tonic-gate 	}
6237c478bd9Sstevel@tonic-gate 	rc->stack = e;
6247c478bd9Sstevel@tonic-gate 	return RET_SUCCESS;
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate /*
6287c478bd9Sstevel@tonic-gate  * bt_rseq --
6297c478bd9Sstevel@tonic-gate  *	Like __bt_seq but does recursive descent tree traversal
6307c478bd9Sstevel@tonic-gate  *	instead of using the prev/next pointers.
6317c478bd9Sstevel@tonic-gate  */
6327c478bd9Sstevel@tonic-gate int
bt_rseq(dbp,key,data,curs,flags)6337c478bd9Sstevel@tonic-gate bt_rseq(dbp, key, data, curs, flags)
6347c478bd9Sstevel@tonic-gate 	const DB *dbp;
6357c478bd9Sstevel@tonic-gate 	DBT *key, *data;
6367c478bd9Sstevel@tonic-gate 	void **curs;
6377c478bd9Sstevel@tonic-gate 	u_int flags;
6387c478bd9Sstevel@tonic-gate {
6397c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
6407c478bd9Sstevel@tonic-gate 	BTREE *t;
6417c478bd9Sstevel@tonic-gate 	EPG e;
6427c478bd9Sstevel@tonic-gate 	int status;
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 	t = dbp->internal;
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	/* Toss any page pinned across calls. */
6477c478bd9Sstevel@tonic-gate 	if (t->bt_pinned != NULL) {
6487c478bd9Sstevel@tonic-gate 		mpool_put(t->bt_mp, t->bt_pinned, 0);
6497c478bd9Sstevel@tonic-gate 		t->bt_pinned = NULL;
6507c478bd9Sstevel@tonic-gate 	}
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	if (curs == NULL) {
6537c478bd9Sstevel@tonic-gate 		errno = EINVAL;
6547c478bd9Sstevel@tonic-gate 		return RET_ERROR;
6557c478bd9Sstevel@tonic-gate 	}
6567c478bd9Sstevel@tonic-gate 	if (*curs == NULL) {
6577c478bd9Sstevel@tonic-gate 		status = bt_rcinit(curs);
6587c478bd9Sstevel@tonic-gate 		if (status != RET_SUCCESS)
6597c478bd9Sstevel@tonic-gate 			return RET_ERROR;
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate 	rc = *curs;
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	 * If scan unitialized as yet, or starting at a specific record, set
6657c478bd9Sstevel@tonic-gate 	 * the scan to a specific key.  Both bt_rseqset and bt_rseqadv pin
6667c478bd9Sstevel@tonic-gate 	 * the page the cursor references if they're successful.
6677c478bd9Sstevel@tonic-gate 	 */
6687c478bd9Sstevel@tonic-gate 	switch (flags) {
6697c478bd9Sstevel@tonic-gate 	case R_NEXT:
6707c478bd9Sstevel@tonic-gate 	case R_PREV:
6717c478bd9Sstevel@tonic-gate 		if (F_ISSET(&rc->cursor, CURS_INIT)) {
6727c478bd9Sstevel@tonic-gate 			status = bt_rseqadv(t, &e, rc, flags);
6737c478bd9Sstevel@tonic-gate 			break;
6747c478bd9Sstevel@tonic-gate 		}
6757c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
6767c478bd9Sstevel@tonic-gate 	case R_FIRST:
6777c478bd9Sstevel@tonic-gate 	case R_LAST:
6787c478bd9Sstevel@tonic-gate 	case R_CURSOR:
6797c478bd9Sstevel@tonic-gate 		status = bt_rseqset(t, &e, key, rc, flags);
6807c478bd9Sstevel@tonic-gate 		break;
6817c478bd9Sstevel@tonic-gate 	default:
6827c478bd9Sstevel@tonic-gate 		errno = EINVAL;
6837c478bd9Sstevel@tonic-gate 		return (RET_ERROR);
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	if (status == RET_SUCCESS) {
6877c478bd9Sstevel@tonic-gate 		status =
6887c478bd9Sstevel@tonic-gate 		    __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0);
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 		/*
6917c478bd9Sstevel@tonic-gate 		 * If the user is doing concurrent access, we copied the
6927c478bd9Sstevel@tonic-gate 		 * key/data, toss the page.
6937c478bd9Sstevel@tonic-gate 		 */
6947c478bd9Sstevel@tonic-gate 		if (F_ISSET(t, B_DB_LOCK))
6957c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, e.page, 0);
6967c478bd9Sstevel@tonic-gate 		else
6977c478bd9Sstevel@tonic-gate 			t->bt_pinned = e.page;
6987c478bd9Sstevel@tonic-gate 	} else if (status == RET_SPECIAL)
6997c478bd9Sstevel@tonic-gate 		bt_rcdestroy(curs);
7007c478bd9Sstevel@tonic-gate 	return (status);
7017c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate /*
7047c478bd9Sstevel@tonic-gate  * bt_rseqset --
7057c478bd9Sstevel@tonic-gate  *	Set the sequential scan to a specific key.
7067c478bd9Sstevel@tonic-gate  *
7077c478bd9Sstevel@tonic-gate  * Parameters:
7087c478bd9Sstevel@tonic-gate  *	t:	tree
7097c478bd9Sstevel@tonic-gate  *	ep:	storage for returned key
7107c478bd9Sstevel@tonic-gate  *	key:	key for initial scan position
7117c478bd9Sstevel@tonic-gate  *	rc:	recursion cursor
7127c478bd9Sstevel@tonic-gate  *	flags:	R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
7137c478bd9Sstevel@tonic-gate  *
7147c478bd9Sstevel@tonic-gate  * Side effects:
7157c478bd9Sstevel@tonic-gate  *	Pins the page the cursor references.
7167c478bd9Sstevel@tonic-gate  *	Updates rc's stack and cursor.
7177c478bd9Sstevel@tonic-gate  *
7187c478bd9Sstevel@tonic-gate  * Returns:
7197c478bd9Sstevel@tonic-gate  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
7207c478bd9Sstevel@tonic-gate  */
7217c478bd9Sstevel@tonic-gate static int
bt_rseqset(t,ep,key,rc,flags)7227c478bd9Sstevel@tonic-gate bt_rseqset(t, ep, key, rc, flags)
7237c478bd9Sstevel@tonic-gate 	BTREE *t;
7247c478bd9Sstevel@tonic-gate 	EPG *ep;
7257c478bd9Sstevel@tonic-gate 	DBT *key;
7267c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
7277c478bd9Sstevel@tonic-gate 	int flags;
7287c478bd9Sstevel@tonic-gate {
7297c478bd9Sstevel@tonic-gate 	PAGE *h;
7307c478bd9Sstevel@tonic-gate 	db_pgno_t pg;
7317c478bd9Sstevel@tonic-gate 	int status;
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	/*
7347c478bd9Sstevel@tonic-gate 	 * Find the first, last or specific key in the tree and point the
7357c478bd9Sstevel@tonic-gate 	 * cursor at it.  The cursor may not be moved until a new key has
7367c478bd9Sstevel@tonic-gate 	 * been found.
7377c478bd9Sstevel@tonic-gate 	 */
7387c478bd9Sstevel@tonic-gate 	switch (flags) {
7397c478bd9Sstevel@tonic-gate 	case R_CURSOR:		/* Not implemented. */
7407c478bd9Sstevel@tonic-gate 		errno = EINVAL;
7417c478bd9Sstevel@tonic-gate 		return RET_ERROR;
7427c478bd9Sstevel@tonic-gate 	case R_FIRST:				/* First record. */
7437c478bd9Sstevel@tonic-gate 	case R_NEXT:
7447c478bd9Sstevel@tonic-gate 		bt_rcclr(rc);
7457c478bd9Sstevel@tonic-gate 		/* Walk down the left-hand side of the tree. */
7467c478bd9Sstevel@tonic-gate 		for (pg = P_ROOT;;) {
7477c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
7487c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 			/* Check for an empty tree. */
7517c478bd9Sstevel@tonic-gate 			if (NEXTINDEX(h) == 0) {
7527c478bd9Sstevel@tonic-gate 				mpool_put(t->bt_mp, h, 0);
7537c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
7547c478bd9Sstevel@tonic-gate 			}
7557c478bd9Sstevel@tonic-gate 
7567c478bd9Sstevel@tonic-gate 			if (h->flags & (P_BLEAF | P_RLEAF))
7577c478bd9Sstevel@tonic-gate 				break;
7587c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, 0)->pgno;
7597c478bd9Sstevel@tonic-gate 			status = bt_rcpush(rc, h->pgno, 0);
7607c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
7617c478bd9Sstevel@tonic-gate 			if (status != RET_SUCCESS)
7627c478bd9Sstevel@tonic-gate 				return status;
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 		ep->page = h;
7657c478bd9Sstevel@tonic-gate 		ep->index = 0;
7667c478bd9Sstevel@tonic-gate 		break;
7677c478bd9Sstevel@tonic-gate 	case R_LAST:				/* Last record. */
7687c478bd9Sstevel@tonic-gate 	case R_PREV:
7697c478bd9Sstevel@tonic-gate 		bt_rcclr(rc);
7707c478bd9Sstevel@tonic-gate 		/* Walk down the right-hand side of the tree. */
7717c478bd9Sstevel@tonic-gate 		for (pg = P_ROOT;;) {
7727c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
7737c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 			/* Check for an empty tree. */
7767c478bd9Sstevel@tonic-gate 			if (NEXTINDEX(h) == 0) {
7777c478bd9Sstevel@tonic-gate 				mpool_put(t->bt_mp, h, 0);
7787c478bd9Sstevel@tonic-gate 				return (RET_SPECIAL);
7797c478bd9Sstevel@tonic-gate 			}
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 			if (h->flags & (P_BLEAF | P_RLEAF))
7827c478bd9Sstevel@tonic-gate 				break;
7837c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
7847c478bd9Sstevel@tonic-gate 			status = bt_rcpush(rc, h->pgno, NEXTINDEX(h) - 1);
7857c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
7867c478bd9Sstevel@tonic-gate 			if (status != RET_SUCCESS)
7877c478bd9Sstevel@tonic-gate 				return status;
7887c478bd9Sstevel@tonic-gate 		}
7897c478bd9Sstevel@tonic-gate 		ep->page = h;
7907c478bd9Sstevel@tonic-gate 		ep->index = NEXTINDEX(h) - 1;
7917c478bd9Sstevel@tonic-gate 		break;
7927c478bd9Sstevel@tonic-gate 	}
7937c478bd9Sstevel@tonic-gate 	rc->cursor.pg.pgno = ep->page->pgno;
7947c478bd9Sstevel@tonic-gate 	rc->cursor.pg.index = ep->index;
7957c478bd9Sstevel@tonic-gate 	F_CLR(&rc->cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
7967c478bd9Sstevel@tonic-gate 	F_SET(&rc->cursor, CURS_INIT);
7977c478bd9Sstevel@tonic-gate 	return (RET_SUCCESS);
7987c478bd9Sstevel@tonic-gate }
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate /*
8017c478bd9Sstevel@tonic-gate  * bt_rseqadvance --
8027c478bd9Sstevel@tonic-gate  *	Advance the sequential scan.
8037c478bd9Sstevel@tonic-gate  *
8047c478bd9Sstevel@tonic-gate  * Parameters:
8057c478bd9Sstevel@tonic-gate  *	t:	tree
8067c478bd9Sstevel@tonic-gate  *	ep:	return page
8077c478bd9Sstevel@tonic-gate  *	rc:	recursion cursor
8087c478bd9Sstevel@tonic-gate  *	flags:	R_NEXT, R_PREV
8097c478bd9Sstevel@tonic-gate  *
8107c478bd9Sstevel@tonic-gate  * Side effects:
8117c478bd9Sstevel@tonic-gate  *	Pins the page the new key/data record is on.
8127c478bd9Sstevel@tonic-gate  *	Updates rc's stack and cursor.
8137c478bd9Sstevel@tonic-gate  *
8147c478bd9Sstevel@tonic-gate  * Returns:
8157c478bd9Sstevel@tonic-gate  *	RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
8167c478bd9Sstevel@tonic-gate  */
8177c478bd9Sstevel@tonic-gate static int
bt_rseqadv(t,ep,rc,flags)8187c478bd9Sstevel@tonic-gate bt_rseqadv(t, ep, rc, flags)
8197c478bd9Sstevel@tonic-gate 	BTREE *t;
8207c478bd9Sstevel@tonic-gate 	EPG *ep;
8217c478bd9Sstevel@tonic-gate 	RCURSOR *rc;
8227c478bd9Sstevel@tonic-gate 	int flags;
8237c478bd9Sstevel@tonic-gate {
8247c478bd9Sstevel@tonic-gate 	CURSOR *c;
8257c478bd9Sstevel@tonic-gate 	PAGE *h;
8267c478bd9Sstevel@tonic-gate 	indx_t idx;
8277c478bd9Sstevel@tonic-gate 	db_pgno_t pg;
8287c478bd9Sstevel@tonic-gate 	int status;
8297c478bd9Sstevel@tonic-gate 	EPGNO *e;
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	/*
8327c478bd9Sstevel@tonic-gate 	 * There are a couple of states that we can be in.  The cursor has
8337c478bd9Sstevel@tonic-gate 	 * been initialized by the time we get here, but that's all we know.
8347c478bd9Sstevel@tonic-gate 	 */
8357c478bd9Sstevel@tonic-gate 	c = &rc->cursor;
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 	/* Get the page referenced by the cursor. */
8387c478bd9Sstevel@tonic-gate 	if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
8397c478bd9Sstevel@tonic-gate 		return (RET_ERROR);
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	/*
8427c478bd9Sstevel@tonic-gate  	 * Find the next/previous record in the tree and point the cursor at
8437c478bd9Sstevel@tonic-gate 	 * it.  The cursor may not be moved until a new key has been found.
8447c478bd9Sstevel@tonic-gate 	 */
8457c478bd9Sstevel@tonic-gate 	switch (flags) {
8467c478bd9Sstevel@tonic-gate 	case R_NEXT:			/* Next record. */
8477c478bd9Sstevel@tonic-gate 		idx = c->pg.index;
8487c478bd9Sstevel@tonic-gate 		while (++idx == NEXTINDEX(h)) {
8497c478bd9Sstevel@tonic-gate 			/* Crawl up if we hit the right edge. */
8507c478bd9Sstevel@tonic-gate 			e = bt_rcpop(rc);
8517c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
8527c478bd9Sstevel@tonic-gate 			if (e == NULL) /* Hit the right edge of root. */
8537c478bd9Sstevel@tonic-gate 				return RET_SPECIAL;
8547c478bd9Sstevel@tonic-gate 			idx = e->index;
8557c478bd9Sstevel@tonic-gate 			pg = e->pgno;
8567c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
8577c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
8587c478bd9Sstevel@tonic-gate 		}
8597c478bd9Sstevel@tonic-gate 		while (!(h->flags & (P_BLEAF | P_RLEAF))) {
8607c478bd9Sstevel@tonic-gate 			/* Crawl down the left until we hit a leaf. */
8617c478bd9Sstevel@tonic-gate 			status = bt_rcpush(rc, h->pgno, idx);
8627c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, idx)->pgno;
8637c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
8647c478bd9Sstevel@tonic-gate 			if (status != RET_SUCCESS)
8657c478bd9Sstevel@tonic-gate 				return status;
8667c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
8677c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
8687c478bd9Sstevel@tonic-gate 			idx = 0;
8697c478bd9Sstevel@tonic-gate 		}
8707c478bd9Sstevel@tonic-gate 		break;
8717c478bd9Sstevel@tonic-gate 	case R_PREV:			/* Previous record. */
8727c478bd9Sstevel@tonic-gate 		idx = c->pg.index;
8737c478bd9Sstevel@tonic-gate 		while (!idx) {
8747c478bd9Sstevel@tonic-gate 			/* Crawl up if we hit the left edge. */
8757c478bd9Sstevel@tonic-gate 			e = bt_rcpop(rc);
8767c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
8777c478bd9Sstevel@tonic-gate 			if (e == NULL) /* Hit the left edge of root. */
8787c478bd9Sstevel@tonic-gate 				return RET_SPECIAL;
8797c478bd9Sstevel@tonic-gate 			idx = e->index;
8807c478bd9Sstevel@tonic-gate 			pg = e->pgno;
8817c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
8827c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
8837c478bd9Sstevel@tonic-gate 		}
8847c478bd9Sstevel@tonic-gate 		idx--;
8857c478bd9Sstevel@tonic-gate 		while (!(h->flags & (P_BLEAF | P_RLEAF))) {
8867c478bd9Sstevel@tonic-gate 			/* Crawl down the right until we hit a leaf. */
8877c478bd9Sstevel@tonic-gate 			status = bt_rcpush(rc, h->pgno, idx);
8887c478bd9Sstevel@tonic-gate 			pg = GETBINTERNAL(h, idx)->pgno;
8897c478bd9Sstevel@tonic-gate 			mpool_put(t->bt_mp, h, 0);
8907c478bd9Sstevel@tonic-gate 			if (status != RET_SUCCESS)
8917c478bd9Sstevel@tonic-gate 				return status;
8927c478bd9Sstevel@tonic-gate 			if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
8937c478bd9Sstevel@tonic-gate 				return (RET_ERROR);
8947c478bd9Sstevel@tonic-gate 			idx = NEXTINDEX(h) - 1;
8957c478bd9Sstevel@tonic-gate 		}
8967c478bd9Sstevel@tonic-gate 		break;
8977c478bd9Sstevel@tonic-gate 	}
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	ep->page = h;
9007c478bd9Sstevel@tonic-gate 	ep->index = idx;
9017c478bd9Sstevel@tonic-gate 	c->pg.pgno = h->pgno;
9027c478bd9Sstevel@tonic-gate 	c->pg.index = idx;
9037c478bd9Sstevel@tonic-gate 	F_CLR(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
9047c478bd9Sstevel@tonic-gate 	F_SET(c, CURS_INIT);
9057c478bd9Sstevel@tonic-gate 	return (RET_SUCCESS);
9067c478bd9Sstevel@tonic-gate }
907