/* ----------------------------------------------------------------
 *   FILE
 * 	genam.c
 *	
 *   DESCRIPTION
 *	general index access method routines
 *
 *   INTERFACE ROUTINES
 *
 *   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 "tmp/postgres.h"

#include "access/attnum.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/itup.h"
#include "access/newam.h"
#include "access/relscan.h"
#include "access/sdir.h"
#include "access/skey.h"

#include "storage/form.h"
#include "utils/log.h"
#include "utils/rel.h"

#include "catalog/catname.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_index.h"
#include "catalog/pg_proc.h"

RcsId("$Header: /private/postgres/src/access/index/RCS/genam.c,v 1.8 1992/03/06 00:17:12 mer Exp $");

/* ----------------------------------------------------------------
 *	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, scanFromEnd, numberOfKeys, key)
    Relation	relation;
    Boolean	scanFromEnd;
    uint16	numberOfKeys;
    ScanKey	key;
{
    IndexScanDesc	scan;

    if (! RelationIsValid(relation))
	elog(WARN, "RelationGetIndexScan: relation invalid");

    scan = (IndexScanDesc)
	palloc(sizeof *scan + (numberOfKeys - 1) * sizeof key->data[0]);

    scan->relation = relation;
    scan->opaque = (Pointer) NULL;
    scan->numberOfKeys = numberOfKeys;

    ItemPointerSetInvalid(&scan->previousItemData);
    ItemPointerSetInvalid(&scan->currentItemData);
    ItemPointerSetInvalid(&scan->nextItemData);
    ItemPointerSetInvalid(&scan->previousMarkData);
    ItemPointerSetInvalid(&scan->currentMarkData);
    ItemPointerSetInvalid(&scan->nextMarkData);

    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(scan, scanFromEnd, key)
    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 = (Boolean) scanFromEnd;

    if (scan->numberOfKeys > 0)
	bcopy((Pointer)& key->data[0],
	      (Pointer)& scan->keyData.data[0],
	      scan->numberOfKeys * sizeof key->data[0]);
}

/* ----------------
 *  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(scan)
    IndexScanDesc	scan;
{
    if (! IndexScanIsValid(scan))
	elog(WARN, "IndexScanEnd: invalid scan");

    pfree((Pointer) 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(scan)
    IndexScanDesc	scan;
{
    RetrieveIndexResult	result;

    if (scan->flags & ScanUncheckedPrevious) {
	result = (RetrieveIndexResult)
	    index_getnext(scan, BackwardScanDirection);

	if (RetrieveIndexResultIsValid(result)) {
	    scan->previousItemData =
		*RetrieveIndexResultGetIndexItemPointer(result);
	} else {
	    ItemPointerSetInvalid(&scan->previousItemData);
	}

    } else if (scan->flags & ScanUncheckedNext) {
	result = (RetrieveIndexResult)
	    index_getnext(scan, ForwardScanDirection);

	if (RetrieveIndexResultIsValid(result)) {
	    scan->nextItemData =
		*RetrieveIndexResultGetIndexItemPointer(result);
	} 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(scan)
    IndexScanDesc	scan;
{	
    RetrieveIndexResult	result;

    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 */
}

/* ----------------
 *  IndexScanGetRetrieveIndexResult 
 *
 *  Return a RetrieveIndexResult for the next tuple in the index scan.
 *  A RetrieveIndexResult (see itup.h) is a index tuple/heap tuple pair.
 *  This routine is used primarily by the vacuum daemon, so that it can
 *  delete index tuples associated with zapped heap tuples.
 *
 *  Parameters:
 *		scan -- Index scan to use.
 *		direction -- Forward, backward, or no movement.
 *
 *  Returns:
 *		RetrieveIndexResult for the tuple in the scan that's next
 *		in the specified direction.
 *
 *  Side Effects: None.
 * ----------------
 */
RetrieveIndexResult
IndexScanGetRetrieveIndexResult(scan, direction)
    IndexScanDesc	scan;
    ScanDirection	direction;
{
    return (RetrieveIndexResult)
	index_getnext(scan, direction);
}

/* ----------------
 *	IndexScanGetGeneralRetrieveIndexResult
 *
 *  	A GeneralRetrieveIndexResult (see itup.h) is a index tuple
 *	rule lock pair.  But currently we don't support index rule
 *	lock results so this routine is pretty bogus.
 * ----------------
 */
GeneralRetrieveIndexResult
IndexScanGetGeneralRetrieveIndexResult(scan, direction)
    IndexScanDesc	scan;
    ScanDirection	direction;
{
    RetrieveIndexResult		result;
    ItemPointer 		heapItemPointer;
    GeneralRetrieveIndexResult	generalResult;

    /* ----------------
     *	use index_getnext to get the "RetrieveIndexResult"
     * ----------------
     */
    result = (RetrieveIndexResult) index_getnext(scan, direction);

    if (! RetrieveIndexResultIsValid(result))
	return (GeneralRetrieveIndexResult) NULL;

    /* ----------------
     *	silly crap to convert a "RetrieveIndexResult" to a
     *  "GeneralRetrieveIndexResult".  
     * ----------------
     */
    heapItemPointer = RetrieveIndexResultGetHeapItemPointer(result);

    generalResult = (GeneralRetrieveIndexResult) palloc(sizeof *result);
    generalResult->heapItemData = *heapItemPointer;

    pfree(result);
    return generalResult;
}
