/* ----------------------------------------------------------------
 *   FILE
 *	indexam.c
 *	
 *   DESCRIPTION
 *	general index access method routines
 *
 *   INTERFACE ROUTINES
 *	index_open 	- open an index relation by relationId
 *	index_openr 	- open a index relation by name
 *	index_close 	- close a index relation
 *	index_beginscan - start a scan of an index
 *	index_rescan  	- restart a scan of an index
 *	index_endscan 	- end a scan
 *	index_insert 	- insert an index tuple into a relation
 *	index_delete 	- delete an item from an index relation
 *	index_markpos  	- mark a scan position
 *	index_restrpos  - restore a scan position
 *	index_getnext 	- get the next tuple from a scan
 * **	index_fetch	- retrieve tuple with tid
 * **	index_replace	- replace a tuple
 * **	index_getattr	- get an attribute from an index tuple
 *	index_getprocid - get a support procedure id from the rel tuple
 *	
 *	IndexScanIsValid - check index scan
 *
 *   NOTES
 *	This file contains the index_ routines which used
 *	to be a scattered collection of stuff in access/genam.
 *
 *	The ** routines: index_fetch, index_replace, and index_getattr
 *	have not yet been implemented.  They may not be needed.
 *
 * 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.
 *
 *  IDENTIFICATION
 *	$Header: /private/postgres/src/access/index/RCS/indexam.c,v 1.15 1992/04/13 18:30:47 mer Exp $
 * ----------------------------------------------------------------
 */

#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 "access/funcindex.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/indexam.c,v 1.15 1992/04/13 18:30:47 mer Exp $");

/* ----------------
 *   undefine macros we aren't going to use that would otherwise
 *   get in our way..  delete is defined in c.h and the am's are
 *   defined in heapam.h
 * ----------------
 */
#undef delete
#undef aminsert
#undef amdelete
#undef ambeginscan
#undef amrescan
#undef amendscan
#undef ammarkpos
#undef amrestrpos
#undef amgettuple

/* ----------------------------------------------------------------
 *		    macros used in index_ routines
 * ----------------------------------------------------------------
 */
#define RELATION_CHECKS \
    Assert(RelationIsValid(relation)); \
    Assert(FormIsValid((Form) relation->rd_am))

#define SCAN_CHECKS \
    Assert(IndexScanIsValid(scan)); \
    Assert(RelationIsValid(scan->relation)); \
    Assert(FormIsValid((Form) scan->relation->rd_am))

#define GET_REL_PROCEDURE(x,y) \
    CppConcat(procedure = relation->rd_am->,y); \
    if (! RegProcedureIsValid(procedure)) \
        elog(WARN, "index_%s: invalid %s regproc", \
	     CppAsString(x), CppAsString(y))

#define GET_SCAN_PROCEDURE(x,y) \
    CppConcat(procedure = scan->relation->rd_am->,y); \
    if (! RegProcedureIsValid(procedure)) \
        elog(WARN, "index_%s: invalid %s regproc", \
	     CppAsString(x), CppAsString(y))


/* ----------------------------------------------------------------
 *		   index_ interface functions
 * ----------------------------------------------------------------
 */
/* ----------------
 *	index_open - open an index relation by relationId
 *
 *	presently the relcache routines do all the work we need
 *	to open/close index relations.
 * ----------------
 */
Relation
index_open(relationId)
    ObjectId	relationId;
{
    return (Relation)
	RelationIdGetRelation(relationId);
}

/* ----------------
 *	index_openr - open a index relation by name
 *
 *	presently the relcache routines do all the work we need
 *	to open/close index relations.
 * ----------------
 */
Relation
index_openr(relationName)
    Name relationName;
{
    return (Relation)
	RelationNameGetRelation(relationName);
}

/* ----------------
 *	index_close - close a index relation
 *
 *	presently the relcache routines do all the work we need
 *	to open/close index relations.
 * ----------------
 */
void
index_close(relation)
    Relation relation;
{
    (void) RelationClose(relation);
}

/* ----------------
 *	index_insert - insert an index tuple into a relation
 * ----------------
 */
GeneralInsertIndexResult
index_insert(relation, indexTuple, offsetOutP)
    Relation	relation;
    IndexTuple	indexTuple;
    double	*offsetOutP;
{
    RegProcedure		procedure;
    InsertIndexResult		specificResult;
    GeneralInsertIndexResult	returnResult;

    RELATION_CHECKS;
    GET_REL_PROCEDURE(insert,aminsert);
    
    /* ----------------
     *	have the am's insert proc do all the work.  
     * ----------------
     */
    specificResult = (InsertIndexResult)
	fmgr(procedure, relation, indexTuple, NULL);

    /* ----------------
     *	the insert proc is supposed to return a "specific result" and
     *  this routine has to return a "general result" so after we get
     *  something back from the insert proc, we allocate a
     *  "general result" and copy some crap between the two.
     *
     *  As far as I'm concerned all this result shit is needlessly c
     *  omplicated and should be eliminated.  -cim 1/19/91
     *
     *  mao concurs.  regardless of how we feel here, however, it is
     *  important to free memory we don't intend to return to anyone.
     *  2/28/91
     * ----------------
     */
    returnResult = (GeneralInsertIndexResult) palloc(sizeof *returnResult);
    returnResult->pointerData = specificResult->pointerData;

    if (PointerIsValid(offsetOutP))
	*offsetOutP = specificResult->offset;
    
    pfree(specificResult);
    return (returnResult);
}

/* ----------------
 *	index_delete - delete an item from an index relation
 * ----------------
 */
void
index_delete(relation, indexItem)
    Relation	relation;
    ItemPointer	indexItem;
{
    RegProcedure	procedure;
    
    RELATION_CHECKS;
    GET_REL_PROCEDURE(delete,amdelete);
    
    (void) fmgr(procedure, relation, indexItem);    
}

/* ----------------
 *	index_beginscan - start a scan of an index
 * ----------------
 */
IndexScanDesc
index_beginscan(relation, scanFromEnd, numberOfKeys, key)
    Relation	relation;
    Boolean	scanFromEnd;
    uint16	numberOfKeys;
    ScanKey	key;
{
    IndexScanDesc	scandesc;
    RegProcedure	procedure;
    
    RELATION_CHECKS;
    GET_REL_PROCEDURE(beginscan,ambeginscan);
    
    RelationSetRIntentLock(relation);

    scandesc = (IndexScanDesc)
	fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
    
    return scandesc;
}

/* ----------------
 *	index_rescan  - restart a scan of an index
 * ----------------
 */
void
index_rescan(scan, scanFromEnd, key)
    IndexScanDesc	scan;
    bool		scanFromEnd;
    ScanKey		key;
{
    RegProcedure	procedure;
    
    SCAN_CHECKS;
    GET_SCAN_PROCEDURE(rescan,amrescan);

    (void) fmgr(procedure, scan, scanFromEnd, key);
}

/* ----------------
 *	index_endscan - end a scan
 * ----------------
 */
void
index_endscan(scan)
    IndexScanDesc	scan;
{
    RegProcedure	procedure;
    
    SCAN_CHECKS;
    GET_SCAN_PROCEDURE(endscan,amendscan);

    (void) fmgr(procedure, scan);

    RelationUnsetRIntentLock(scan->relation);
}

/* ----------------
 *	index_markpos  - mark a scan position
 * ----------------
 */
void
index_markpos(scan)
    IndexScanDesc	scan;
{
    RegProcedure	procedure;
    
    SCAN_CHECKS;
    GET_SCAN_PROCEDURE(markpos,ammarkpos);

    (void) fmgr(procedure, scan);
}

/* ----------------
 *	index_restrpos  - restore a scan position
 * ----------------
 */
void
index_restrpos(scan)
    IndexScanDesc	scan;
{
    RegProcedure	procedure;
    
    SCAN_CHECKS;
    GET_SCAN_PROCEDURE(restrpos,amrestrpos);

    (void) fmgr(procedure, scan);
}

/* ----------------
 *	index_getnext - get the next tuple from a scan
 *
 *  	A RetrieveIndexResult is a index tuple/heap tuple pair
 * ----------------
 */
RetrieveIndexResult
index_getnext(scan, direction)
    IndexScanDesc	scan;
    ScanDirection	direction;
{
    RegProcedure		procedure;
    RetrieveIndexResult		result;

    SCAN_CHECKS;
    GET_SCAN_PROCEDURE(getnext,amgettuple);

    /* ----------------
     *	have the am's gettuple proc do all the work.  
     * ----------------
     */
    result = (RetrieveIndexResult)
	fmgr(procedure, scan, direction);

    if (! RetrieveIndexResultIsValid(result))
	return NULL;
    
    return result;
}
/* ----------------
 *	index_getprocid
 *
 *	Some indexed access methods may require support routines that are
 *	not in the operator class/operator model imposed by pg_am.  These
 *	access methods may store the OIDs of registered procedures they
 *	need in pg_amproc.  These registered procedure OIDs are ordered in
 *	a way that makes sense to the access method, and used only by the
 *	access method.  The general index code doesn't know anything about
 *	the routines involved; it just builds an ordered list of them for
 *	each attribute on which an index is defined.
 *
 *	This routine returns the requested procedure OID for a particular
 *	indexed attribute.
 * ----------------
 */

RegProcedure
index_getprocid(irel, attnum, procnum)
    Relation irel;
    AttributeNumber attnum;
    uint16 procnum;
{
    RegProcedure *loc;
    AttributeNumber natts;

    natts = irel->rd_rel->relnatts;

    loc = *((RegProcedure **)
       (((char *) &(irel->rd_att.data[natts])) + sizeof(IndexStrategy)));

    Assert(loc != NULL);

    return (loc[(natts * (procnum - 1)) + (attnum - 1)]);
}

/* ----------------
 *	InsertIndexTuple
 * ----------------
 */
void
InsertIndexTuple(heapRelation, indexRelation, numberOfAttributes, 
		 attributeNumber, heapTuple, 
		 indexStrategy, parameterCount, parameter)
    Relation		heapRelation;
    Relation		indexRelation;
    AttributeNumber	numberOfAttributes;
    AttributeNumber	attributeNumber[];
    HeapTuple          	heapTuple;
    IndexStrategy	indexStrategy;
    uint16		parameterCount;
    Datum		parameter[];
{
    HeapScanDesc	scan;
    Buffer		buffer;
    AttributeNumber	attributeIndex;
    IndexTuple		indexTuple;
    TupleDescriptor	heapDescriptor;
    TupleDescriptor	indexDescriptor;
    Datum		*datum;
    Boolean		*nullv;

    char		*dom[2];
    extern		HasCached;
    extern char		CachedClass[];
    extern		fillCached();
    GeneralInsertIndexResult	insertResult;

    /* more & better checking is needed */
    Assert(ObjectIdIsValid(indexRelation->rd_rel->relam));	/* XXX */

    datum = (Datum *)   palloc(numberOfAttributes * sizeof *datum);
    nullv =  (Boolean *) palloc(numberOfAttributes * sizeof *nullv);
    
    heapDescriptor =  RelationGetTupleDescriptor(heapRelation);
    indexDescriptor = RelationGetTupleDescriptor(indexRelation);

    Assert(HeapTupleIsValid(heapTuple));
    for (attributeIndex = 1; attributeIndex <= numberOfAttributes;
	 attributeIndex += 1) {
	  
	AttributeOffset	attributeOffset;
	Boolean		attributeIsNull;
	  
	attributeOffset = AttributeNumberGetAttributeOffset(attributeIndex);

	datum[attributeOffset] =
	    PointerGetDatum( heap_getattr(heapTuple, buffer,
					  attributeNumber[attributeOffset],
					  heapDescriptor, &attributeIsNull) );
	
	  nullv[attributeOffset] = (attributeIsNull) ? 'n' : ' ';
    }

    indexTuple = (IndexTuple)
	index_formtuple(numberOfAttributes,
			indexDescriptor,
			datum,
			nullv);

    indexTuple->t_tid = heapTuple->t_ctid;

    insertResult = index_insert(indexRelation, indexTuple, (double *) 0);
	
    pfree(indexTuple);
    pfree(nullv);
    pfree(datum);
}

/* ----------------
 *	GetHeapTuple
 * ----------------
 */
HeapTuple
GetHeapTuple(result, heaprel, buffer)
    GeneralRetrieveIndexResult      result;
    Relation                        heaprel;
    Buffer                          buffer;
{
    ItemPointer     pointer;
    HeapTuple       tuple;

    Assert(GeneralRetrieveIndexResultIsValid(result));

    pointer = GeneralRetrieveIndexResultGetHeapItemPointer(result);

    if (! ItemPointerIsValid(pointer))
	return NULL;

    tuple = heap_fetch(heaprel, NowTimeQual, pointer, &buffer);

    if (! HeapTupleIsValid(tuple)) 
	return(NULL);
    else 
	return(tuple);
}

Datum
GetIndexValue(tuple, hTupDesc, attOff, attrNums, fInfo, attNull, buffer)
	HeapTuple tuple;
	TupleDescriptor hTupDesc;
	AttributeOffset attOff;
	AttributeNumber attrNums[];
	FuncIndexInfo *fInfo;
	Boolean *attNull;
	Buffer buffer;
{
    Datum returnVal;

    if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidObjectId)
    {
	int i;
	Datum *attData = (Datum *)palloc(FIgetnArgs(fInfo)*sizeof(Datum));

	for (i = 0; i < FIgetnArgs(fInfo); i++)
	{
	    attData[i] = (Datum) heap_getattr(tuple, buffer, attrNums[i], 
					      hTupDesc, attNull);
	}
	returnVal = (Datum)fmgr_array_args(FIgetProcOid(fInfo),
					   FIgetnArgs(fInfo),
					   attData);
	pfree(attData);
	*attNull = FALSE;
    }
    else
    {
	returnVal = (Datum) heap_getattr(tuple, buffer, attrNums[attOff], 
					 hTupDesc, attNull);
    }
    return returnVal;
}
