/*===================================================================
 *
 * FILE:
 *   prs2retrieve.c
 *
 * IDENTIFICATION:
 *   $Header: /private/postgres/src/rules/prs2/RCS/prs2retrieve.c,v 1.16 1991/05/09 18:10:16 sp Exp $
 *
 * DESCRIPTION:
 *   The main routine is prs2Retrieve() which is called whenever
 *   a retrieve operation is performed by the executor.
 *
 *
 *===================================================================
 */

#include "tmp/c.h"
#include "utils/log.h"
#include "rules/prs2.h"
#include "nodes/execnodes.h"	/* which includes access/rulescan.h */

/*-------------------------------------------------------------------
 *
 * prs2Retrieve
 */
Prs2Status
prs2Retrieve(prs2EStateInfo, relationRuleInfo,
	    explainRelation, tuple, buffer, attributeArray,
	    numberOfAttributes, relation, returnedTupleP, returnedBufferP)
Prs2EStateInfo prs2EStateInfo;
RelationRuleInfo relationRuleInfo;
Relation explainRelation;
HeapTuple tuple;
Buffer buffer;
AttributeNumberPtr attributeArray;
int numberOfAttributes;
Relation relation;
HeapTuple *returnedTupleP;
Buffer *returnedBufferP;
{
    RuleLock locks, locksInTuple, locksInRelation;
    int i;
    AttributeValues attrValues;
    int newTupleMade;
    Name relName;
    bool insteadRuleFound;
    bool insteadRuleFoundThisTime;


    /*
     * Find the locks of the tuple.
     *
     * There are 2 places to look for locks. The "tuple-level"
     * locks are stored in the tuple, and the "relation-level"
     * locks are stored in the PG_RELATION relation, but we
     * have also copied them (for efficiency) in 'relationRuleInfo'
     * when initializing the plan.
     *
     * BTW, ignore the tuple locks if the appropriate flag
     * is set in 'relationRuleInfo'. This happens when we scan
     * "pg_class".
     */
    locksInRelation = relationRuleInfo->relationLocks;
    if (relationRuleInfo->ignoreTupleLocks) {
	locksInTuple = prs2MakeLocks();
    } else {
	locksInTuple = prs2GetLocksFromTuple(tuple, buffer);
    }
    locks = prs2LockUnion(locksInRelation, locksInTuple);
    prs2FreeLocks(locksInTuple);

    /*
     * If there are no rules, then return immediatelly...
     */
    if (prs2GetNumberOfLocks(locks)==0) {
	prs2FreeLocks(locks);
	return(PRS2_STATUS_TUPLE_UNCHANGED);
    }

    /*
     * now extract from the tuple the array of the attribute values.
     */
    attrValues = attributeValuesCreate(tuple, buffer, relation);

    /*
     * First activate all the 'late evaluation' rules, i.e.
     * all the rules that try to update the fields to be retrieved.
     */
    for(i=0; i<numberOfAttributes; i++) {
	prs2ActivateBackwardChainingRules(
		    prs2EStateInfo,
		    explainRelation,
		    relation,
		    attributeArray[i],
		    PRS2_OLD_TUPLE,
		    tuple->t_oid,
		    attrValues,
		    locks,
		    LockTypeRetrieveWrite,
		    InvalidObjectId,
		    InvalidAttributeValues,
		    InvalidRuleLock,
		    LockTypeInvalid,
		    attributeArray,
		    numberOfAttributes);
    }

    /*
     * Then, activate all the 'forward chaining rules'
     */
    insteadRuleFound = false;
    for(i=0; i<numberOfAttributes; i++) {
	prs2ActivateForwardChainingRules(
		    prs2EStateInfo,
		    explainRelation,
		    relation,
		    attributeArray[i],
		    LockTypeRetrieveAction,
		    PRS2_OLD_TUPLE,
		    tuple->t_oid,
		    attrValues,
		    locks,
		    LockTypeRetrieveWrite,
		    InvalidObjectId,
		    InvalidAttributeValues,
		    InvalidRuleLock,
		    LockTypeInvalid,
		    &insteadRuleFoundThisTime,
		    attributeArray,
		    numberOfAttributes);
	if (insteadRuleFoundThisTime) {
	    insteadRuleFound = true;
	}
    }

    /*
     * Now all the correct values are in the attrValues array.
     * Create a new tuple with the correct values.
     */
    newTupleMade = attributeValuesMakeNewTuple(
				tuple, buffer,
				attrValues, locks, LockTypeRetrieveWrite,
				relation, returnedTupleP);

    prs2FreeLocks(locks);

    attributeValuesFree(attrValues, relation);

    if (insteadRuleFound) {
	return(PRS2_STATUS_INSTEAD);
    }

    if (newTupleMade) {
	*returnedBufferP = InvalidBuffer;
	/*
	 * The new tuple must have the same locks as the old
	 * tuple (in case we are retrieving the "lock" attribute.
	 */
	locksInTuple = prs2GetLocksFromTuple(tuple, buffer);
	HeapTupleSetRuleLock(*returnedTupleP, InvalidBuffer, locksInTuple);
	return(PRS2_STATUS_TUPLE_CHANGED);
    } else {
	return(PRS2_STATUS_TUPLE_UNCHANGED);
    }
}
