/*-------------------------------------------------------------------------
 *
 * genam.c--
 *    general index access method routines
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.6 1996/11/05 10:02:02 scrappy Exp $
 *
 * NOTES
 *    many of the old access method routines have been turned into
 *    macros and moved to genam.h -cim 4/30/91
 *
 *-------------------------------------------------------------------------
 */
/*
 * OLD COMMENTS
 * Scans are implemented as follows:
 *
 * `0' represents an invalid item pointer.
 * `-' represents an unknown item pointer.
 * `X' represents a known item pointers.
 * `+' represents known or invalid item pointers.
 * `*' represents any item pointers.
 *
 * State is represented by a triple of these symbols in the order of
 * previous, current, next.  Note that the case of reverse scans works
 * identically.
 *
 *	State	Result
 * (1)	+ + -	+ 0 0		(if the next item pointer is invalid)
 * (2)		+ X -		(otherwise)
 * (3)	* 0 0	* 0 0		(no change)
 * (4)	+ X 0	X 0 0		(shift)
 * (5)	* + X	+ X -		(shift, add unknown)
 *
 * All other states cannot occur.
 *
 * Note:
 *It would be possible to cache the status of the previous and
 *	next item pointer using the flags.
 * ----------------------------------------------------------------
 */

#include <postgres.h>

#include <utils/catcache.h>
#include <access/genam.h>
#include <storage/bufmgr.h>

#ifndef HAVE_MEMMOVE
# include <regex/utils.h>
#else
# include <string.h>
#endif

/* ----------------------------------------------------------------
 *	general access method routines
 *
 *	All indexed access methods use an identical scan structure.
 *	We don't know how the various AMs do locking, however, so we don't
 *	do anything about that here.
 *
 *	The intent is that an AM implementor will define a front-end routine
 *	that calls this one, to fill in the scan, and then does whatever kind
 *	of locking he wants.
 * ----------------------------------------------------------------
 */

/* ----------------
 *  RelationGetIndexScan -- Create and fill an IndexScanDesc.
 *
 *	This routine creates an index scan structure and sets its contents
 *	up correctly. This routine calls AMrescan to set up the scan with
 *	the passed key.
 *
 *	Parameters:
 *		relation -- index relation for scan.
 *		scanFromEnd -- if true, begin scan at one of the index's
 *			       endpoints.
 *		numberOfKeys -- count of scan keys (more than one won't
 *				necessarily do anything useful, yet).
 *		key -- the ScanKey for the starting position of the scan.
 *
 *	Returns:
 *		An initialized IndexScanDesc.
 *
 *	Side Effects:
 *		Bumps the ref count on the relation to keep it in the cache.
 *	
 * ----------------
 */
IndexScanDesc
RelationGetIndexScan(Relation relation,
		     bool scanFromEnd,
		     uint16 numberOfKeys,
		     ScanKey key)
{
    IndexScanDesc	scan;
    
    if (! RelationIsValid(relation))
	elog(WARN, "RelationGetIndexScan: relation invalid");
    
    scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
    
    scan->relation = relation;
    scan->opaque = NULL;
    scan->numberOfKeys = numberOfKeys;
    
    ItemPointerSetInvalid(&scan->previousItemData);
    ItemPointerSetInvalid(&scan->currentItemData);
    ItemPointerSetInvalid(&scan->nextItemData);
    ItemPointerSetInvalid(&scan->previousMarkData);
    ItemPointerSetInvalid(&scan->currentMarkData);
    ItemPointerSetInvalid(&scan->nextMarkData);

    if (numberOfKeys > 0) {
	scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * numberOfKeys);
    } else {
	scan->keyData = NULL;
    }

    index_rescan(scan, scanFromEnd, key);
    
    return (scan);
}

/* ----------------
 *  IndexScanRestart -- Restart an index scan.
 *
 *	This routine isn't used by any existing access method.  It's
 *	appropriate if relation level locks are what you want.
 *
 *  Returns:
 *	None.
 *
 *  Side Effects:
 *	None.
 * ----------------
 */
void
IndexScanRestart(IndexScanDesc scan,
		 bool scanFromEnd,
		 ScanKey key)
{
    if (! IndexScanIsValid(scan))
	elog(WARN, "IndexScanRestart: invalid scan");
    
    ItemPointerSetInvalid(&scan->previousItemData);
    ItemPointerSetInvalid(&scan->currentItemData);
    ItemPointerSetInvalid(&scan->nextItemData);
    
    if (RelationGetNumberOfBlocks(scan->relation) == 0) 
	scan->flags = ScanUnmarked;
    else if (scanFromEnd)
	scan->flags = ScanUnmarked | ScanUncheckedPrevious;
    else
	scan->flags = ScanUnmarked | ScanUncheckedNext;
    
    scan->scanFromEnd = (bool) scanFromEnd;
    
    if (scan->numberOfKeys > 0)
	memmove(scan->keyData,
		key,
		scan->numberOfKeys * sizeof(ScanKeyData));
}

/* ----------------
 *  IndexScanEnd -- End and index scan.
 *
 *	This routine is not used by any existing access method, but is
 *	suitable for use if you don't want to do sophisticated locking.
 *
 *  Returns:
 *	None.
 *
 *  Side Effects:
 *	None.
 * ----------------
 */
void
IndexScanEnd(IndexScanDesc scan)
{
    if (! IndexScanIsValid(scan))
	elog(WARN, "IndexScanEnd: invalid scan");
    
    pfree(scan);
}

/* ----------------
 *  IndexScanMarkPosition -- Mark current position in a scan.
 *
 *	This routine isn't used by any existing access method, but is the
 *	one that AM implementors should use, if they don't want to do any
 *	special locking.  If relation-level locking is sufficient, this is
 *	the routine for you.
 *
 *  Returns:
 *	None.
 *
 *  Side Effects:
 *	None.
 * ----------------
 */
void
IndexScanMarkPosition(IndexScanDesc scan)
{
    RetrieveIndexResult	result;
    
    if (scan->flags & ScanUncheckedPrevious) {
	result = 
	    index_getnext(scan, BackwardScanDirection);
	
	if (result != NULL) {
	    scan->previousItemData = result->index_iptr;
	} else {
	    ItemPointerSetInvalid(&scan->previousItemData);
	}
	
    } else if (scan->flags & ScanUncheckedNext) {
	result = (RetrieveIndexResult)
	    index_getnext(scan, ForwardScanDirection);
	
	if (result != NULL) {
	    scan->nextItemData = result->index_iptr;
	} else {
	    ItemPointerSetInvalid(&scan->nextItemData);
	}
    }
    
    scan->previousMarkData = scan->previousItemData;
    scan->currentMarkData = scan->currentItemData;
    scan->nextMarkData = scan->nextItemData;
    
    scan->flags = 0x0;	/* XXX should have a symbolic name */
}

/* ----------------
 *  IndexScanRestorePosition -- Restore position on a marked scan.
 *
 *	This routine isn't used by any existing access method, but is the
 *	one that AM implementors should use if they don't want to do any
 *	special locking.  If relation-level locking is sufficient, then
 *	this is the one you want.
 *
 *  Returns:
 *	None.
 *
 *  Side Effects:
 *	None.
 * ----------------
 */
void
IndexScanRestorePosition(IndexScanDesc scan)
{	
    if (scan->flags & ScanUnmarked) 
	elog(WARN, "IndexScanRestorePosition: no mark to restore");
    
    scan->previousItemData = scan->previousMarkData;
    scan->currentItemData = scan->currentMarkData;
    scan->nextItemData = scan->nextMarkData;
    
    scan->flags = 0x0;	/* XXX should have a symbolic name */
}
