head	1.13;
access;
symbols
	Version_2_1:1.9;
locks; strict;
comment	@ * @;


1.13
date	91.11.18.22.21.22;	author mer;	state Exp;
branches;
next	1.12;

1.12
date	91.08.27.14.40.17;	author sp;	state Exp;
branches;
next	1.11;

1.11
date	91.08.14.15.41.27;	author sp;	state Exp;
branches;
next	1.10;

1.10
date	91.04.08.18.14.10;	author sp;	state Exp;
branches;
next	1.9;

1.9
date	91.02.08.17.37.48;	author sp;	state Exp;
branches;
next	1.8;

1.8
date	91.01.17.19.05.03;	author sp;	state Exp;
branches;
next	1.7;

1.7
date	91.01.09.19.05.58;	author sp;	state Exp;
branches;
next	1.6;

1.6
date	90.10.16.17.17.43;	author sp;	state Exp;
branches;
next	1.5;

1.5
date	90.09.25.16.42.12;	author kemnitz;	state Exp;
branches;
next	1.4;

1.4
date	90.04.03.17.08.36;	author sp;	state Exp;
branches;
next	1.3;

1.3
date	90.03.07.17.16.54;	author sp;	state Exp;
branches;
next	1.2;

1.2
date	90.01.31.03.44.58;	author sp;	state Exp;
branches;
next	1.1;

1.1
date	90.01.11.14.42.04;	author sp;	state Exp;
branches;
next	;


desc
@some routines used to stroe the values of attributes in an
array so that they can be easily changed without having
to make copies of tuples all the time...
@


1.13
log
@prototypes finale
@
text
@
/*==================================================================
 *
 * FILE:
 *   prs2attr.c
 *
 * IDENTIFICATION:
 *$Header: /users/mer/postgres/src/rules/prs2/RCS/prs2attr.c,v 1.12 1991/08/27 14:40:17 sp Exp mer $
 *
 * DESCRIPTION:
 *
 *==================================================================
 */

#include "tmp/c.h"
#include "utils/palloc.h"
#include "utils/log.h"
#include "access/ftup.h"	/* ModifyHeapTuple() defined here... */
#include "rules/prs2.h"

/*---------------------------------------------------------------------
 *
 * attributeValuesCreate
 *
 * Given a tuple create the corresponding 'AttributeValues' array.
 * Note that in the beginnign all the 'value' entries are copied
 * form the tuple and that 'isCalculated' are all false.
 *
 */

AttributeValues
attributeValuesCreate(tuple, buffer, relation)
HeapTuple tuple;
Buffer buffer;
Relation relation;
{
    AttributeNumber i;
    AttributeNumber natts;
    AttributeValues attrValues;
    long size;
    Boolean isNull;

    natts = RelationGetNumberOfAttributes(relation);

    size = natts * sizeof(AttributeValuesData);
    attrValues = (AttributeValues) palloc(size);
    if (attrValues == NULL) {
	elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
    }

    for (i=1; i<=natts; i++) {
	attrValues[i-1].value = HeapTupleGetAttributeValue(
				tuple,
				buffer,
				i,
				RelationGetTupleDescriptor(relation),
				&isNull);
	attrValues[i-1].isCalculated = (Boolean) 0;
	attrValues[i-1].isChanged = (Boolean) 0;
	attrValues[i-1].isNull = isNull;
    }

    return(attrValues);
}

/*---------------------------------------------------------------------
 *
 * attributeValuesFree
 *
 * Free a previously allocated 'AttributeValues' array.
 */
void
attributeValuesFree(a, relation)
AttributeValues a;
Relation relation;
{
    int i;
    AttributeNumber natts;
    TupleDescriptor tdesc;

    /*
     * free all space (if any) occupied by datums generated
     * by "prs2RunOnePlanAndGetValue()"
     * All these datums are creted via "datumCopy()"
     */
    tdesc = RelationGetTupleDescriptor(relation);
    natts = RelationGetNumberOfAttributes(relation);
    for(i=1; i<=natts; i++) {
	if (a[i-1].isCalculated && a[i-1].isChanged) {
	    if (!a[i-1].isNull)
		datumFree(a[i-1].value,
		    tdesc->data[i-1]->atttypid,		/* type */
		    tdesc->data[i-1]->attbyval,		/* byVal */
		    (Size) tdesc->data[i-1]->attlen);	/* type length */
	}
    }

    pfree((Pointer)a);
}

/*--------------------------------------------------------------------
 *
 * attributeValuesMakeNewTuple
 *
 * Given the old tuple and the values that the new tuple should have
 * create a new tuple.
 *
 * There are a couple of tricky things in here...
 *
 * Some of the attributes of the tuple (not necessarily all of them
 * though..) have been checked for rules, i.e. we have checked if there
 * is any rule that calculates a value for this attribute.
 * All these checked attributes have the field 'isCalculated' (of their
 * corresponding entry in array 'attrValues') equal to true.
 * On top of that, if there was an applicable rule, then the field
 * 'isChanged' is true (todenote that the value of this attribute
 * has changed because of a rule activation, therefore the value
 * stored in 'tuple' is now obsolete).
 * If no applicable rule was found then 'isChanged' is equal to false
 * and the value as stored in 'tuple' is the correct one.
 *
 * Finally all attributes not checked for rule activations (because
 * we don't need to calculate their values for the time being...)
 * have their 'isCalculated' field equal to false.
 *
 * Obviously enough, if at least one attribute has isChanged==true,
 * we must create a new tuple with the correct values in it (we
 * use 'ModifyHeapTuple()' for this purpose).
 * If no attribute has been changed we can use the olt 'tuple'
 * thus avoiding the copy (efficiency!).
 *
#ifdef CHANGE_LOCKS
    ---------------------------------------------------
    XXX Currently we do NOT do that !!!         XXXXXXX
    ---------------------------------------------------
 * However we must also need change the locks of the tuple!
 * Say, if during a retrieve operation, a rule that had a lock of
 * type `LockTypeRetrieveWrite' has been activated and calculated a new
 * value for this attribute, then we must remove this lock, so that if
 * later on (at a higher node in the plan, maybe a join or a topmost
 * result node that is used when the user's operation is an append
 * or delete) we want to find the value of this (laready claculated) 
 * attribute, we do not unnecessarily reactivate the rule.
 * Another case where this happens is when we append a tuple.
 * We have to activate all rules that have `LockTypeAppendWrite' locks,
 * but then we can remove these locks because they are no longer
 * needed (a tuple is only appended once!).
 * The replace case is similar....
 *
 * XXX:
 * Hm.. here comes the tricky question. If no attribute has been
 * changed (i.e. all the 'isChanged' are false) but we must change
 * the locks of the tuple, what shall we do??
 * I think that we have the following options, but I don't know
 * which ones are gonna work and which not:
 *    1) We do not copy the tuple, and we just change its
 *       lock so that it points to the new lock:
 *       tuple->t_lcok.l_lock = newLock;
 *    2) We create a new copy of the tuple with the right locks.
 * I decided to do option # 2.
#else
 * If a new tuple is created, then it will always have an empty
 * lock in it.
#endif CHANGE_LOCKS
 *  
 *
 * This routine returns 0 if no tuple copy has been made and
 * therefore the old 'tuple' can be used, or 1 if there was
 * a tuple copy, in which case the 'newTupleP' point to
 * the new tuple (note: in this case the buffer for this
 * new tuple is the 'InvalidBuffer')
 */

int
attributeValuesMakeNewTuple(tuple, buffer, attrValues,
			    locks, lockType, relation, newTupleP)
HeapTuple tuple;
Buffer buffer;
AttributeValues attrValues;
RuleLock locks;
Prs2LockType lockType;
Relation relation;
HeapTuple *newTupleP;
{
    AttributeNumber natts;
    Datum *replaceValue;
    char *replaceNull;
    char *replace;
    long size;
    int i;
    bool tupleHasChanged;
    bool locksHaveToChange;
    HeapTuple t;
    int nlocks;
    RuleLock newLocks;
    Prs2OneLock oneLock;
    AttributeNumber attrNo;
    Prs2LockType thisLockType;

    /*
     * find the number of attributes of the tuple
     */
    natts = RelationGetNumberOfAttributes(relation);

    /*
     * Find if there was any attribute change and/or we have
     * to change the locks...
     * 
     */
    tupleHasChanged = false;
    for (i=0; i<natts; i++) {
	if (attrValues[i].isCalculated && attrValues[i].isChanged) {
	    tupleHasChanged = true;
	    break;
	}
    }

    if (tupleHasChanged) {
	/*
	 * Yes, this tuple has changed because of a rule
	 * activation. Create a new tuple with the
	 * right attribute values
	 *
	 * First create some arrays needed by 'ModifyHeapTuple()'
	 */
	size = natts * sizeof(Datum);
	replaceValue = (Datum *) palloc(size);
	if (replaceValue == NULL) {
	    elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
	}

	size = natts * sizeof(char);
	replaceNull = (char *) palloc(size);
	if (replaceNull == NULL) {
	    elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
	}

	size = natts * sizeof(Datum);
	replace = (char *) palloc(size);
	if (replace == NULL) {
	    elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
	}

	/*
	 * Fill these arrays with the appropriate values
	 */
	for (i=0; i<natts; i++) {
	    if (attrValues[i].isCalculated && attrValues[i].isChanged) {
		replaceValue[i] = attrValues[i].value;
		replace[i] = 'r';
		if (attrValues[i].isNull) {
		    replaceNull[i] = 'n';
		} else {
		    replaceNull[i] = ' ';
		}
	    } else {
		replace[i] = ' ';
		replaceValue[i] = attrValues[i].value;
		if (attrValues[i].isNull) {
		    replaceNull[i] = 'n';
		} else {
		    replaceNull[i] = ' ';
		}
	    }
	}

	/*
	 * create the new tuple
	 */
	*newTupleP = heap_modifytuple( tuple, buffer,
			    relation, replaceValue,
			    replaceNull, replace);
	/*
	 * free the space occupied by the arrays
	 */
	pfree((Pointer)replaceValue);
	pfree(replaceNull);
	pfree(replace);
    }


#ifdef CHANGE_LOCKS
    nlocks = prs2GetNumberOfLocks(locks);

    newLocks = prs2MakeLocks();

    locksHaveToChange = false;
    for (i=0; i<nlocks; i++) {
	oneLock = prs2GetOneLockFromLocks(locks, i);
	attrNo = prs2OneLockGetAttributeNumber(oneLock);
	thisLockType = prs2OneLockGetLockType(oneLock);
	if (lockType != thisLockType ||
	    !attrValues[attrNo-1].isCalculated){
	    /*
	     * Copy this lock...
	     */
	    newLocks = prs2AddLock(newLocks,
			    prs2OneLockGetRuleId(oneLock),
			    prs2OneLockGetLockType(oneLock),
			    prs2OneLockGetAttributeNumber(oneLock),
			    prs2OneLockGetPlanNumber(oneLock),
			    prs2OneLockGetPartialIndx(oneLock),
			    prs2OneLockGetNPartial(oneLock));
	} else {
	    locksHaveToChange = true;
	}
    }
    /*
     * XXX: For the time being NO locks are stored in tuple,
     * they all come from the RelationRelation.
     * So, WE HAVE to put the locks in the tuple anyway...
     */
    if (prs2GetNumberOfLocks(newLocks) !=0) {
	locksHaveToChange = true;
    }

    if (locksHaveToChange && tupleHasChanged) {
	prs2PutLocksInTuple(
			    *newTupleP, InvalidBuffer,
			    relation, newLocks);
	return(1);
    } else if (!locksHaveToChange && tupleHasChanged) {
	/*
	 * this is impossible
	 */
	elog(WARN,"attributeValuesMakeNewTuple: Internal error");
    } else if (locksHaveToChange && !tupleHasChanged) {
	prs2PutLocksInTuple( tuple, buffer,
			    relation, newLocks);
	*newTupleP = tuple;
	return(1);
    } else if (!locksHaveToChange && !tupleHasChanged) {
	if (newLocks != InvalidRuleLock)
	    prs2FreeLocks(newLocks);
	return(0);
    }
#else
    if (tupleHasChanged) {
	/*
	 * NOTE: the new tuple, has NO LOCKS IN IT!
	 * (well, actually it has an empty lock).
	 */
	return(1);
    } else {
	return(0);
    }
#endif CHANGE_LOCKS
}

/*--------------------------------------------------------------------
 *
 * attributeValuesCombineNewAndOldTuple
 *
 * Hm.. this is a little bit tricky, so I'll try to do my best:
 * When we want to replace a tuple, the executor has first to retrieve
 * it. So it calls the Access Methods and retrieves what we call the
 * 'raw' tuple, i.e. the tuple as stored in the database.
 * Then it calls the rule manager to activate any 'ON RETRIEVE' rules,
 * and if there are some backward chaining rules that calculate values
 * for some attributes of this tuple, then the rule manager forms a
 * new tuple. This is what we call the 'old' tuple.
 * (NOTE: both the 'old' and 'raw' tuples are stored in the
 * 'qualification_tuple' and 'raw_qualification_tuple' fields of 
 * the 'EState').
 * Finally this 'old' tuple (if it satisfies the qualifications of the
 * replace command) must be replaced. So, the executor forms a new tuple,
 * which is a copy of the 'old' tuple + the updates proposed by the
 * 'replace' command as issued by the user.
 * Then, a final call to the rule manager is made to activate all the
 * 'ON REPLACE' rules. Some of them might be backward chaining rules of
 * the form 'ON REPLACE EMP.salary WHERE .... DO REPLACE CURRENT(desk = ...)'
 * so they might cause even more updated to the tuple and possibly trigger
 * even more rules etc, and to cut a long story short the whole hell can
 * break loose. My completely bug free code handles all these cases
 * in the most perfect way, and what we end up having is:
 * the 'raw' tuple as described above, and a brand 'new' tuple which 
 * incorporates a) all changes made by ON RETRIEVE rules, b) all user
 * updates and c) all changes made by ON REPLACE rules.
 * Well, the tuple that has to be stored in the database (lets call
 * it the 'returned' tuple) must ONLY incorporate (b) and (c).
 * IT MUST NOT INCORPORATE changes made by ON RETRIEVE rules !!!
 * In order to convince all of you that are not as clever as me and
 * can not see the light, here is an example:
 * We have the rule:
 *   ON RETRIEVE TO EMP.salary DO INSTEAD RETRIEVE (salary = 1)
 * and the tuple:
 *   name="spyros", salary = 9999999999999999, status = "prophet"
 * and the user 'replace' command:
 *   replace EMP(status = "god") where EMP.name = "spyros"
 * So the 'raw' tuple is:
 *   name="spyros", salary = 9999999999999999, status = "prophet"
 * the 'old' tuple (after the ON RETRIEVE rule is activated)
 *   name="spyros", salary = 1, status = "prophet"
 * and the 'new' tuple as formed by the executor (after making all changes
 * suggested by the 'replace' command):
 *   name="spyros", salary = 1, status = "god"
 * 
 * Obviously enough, if we store this tuple in the database and
 * then delete the rule, then spyros' salary will be '1' and not
 * what he really deserves (i.e. '9999999999999999').
 * 
 * So, what we have to do, is somehow keep track of what attributes
 * of the 'new' tuple have been changed either by the user ('status')
 * or by ON REPLACE rules (none in our example), and when forming the
 * 'returned' tuple we must use for these attributes the values stored
 * in 'new' tuple, while for all other attributes we have to use
 * the values as stored in 'raw' tuple.
 *
 * In case you haven't suspected it so far, that's exactly what this
 * routine is supposed to do....
 */

HeapTuple
attributeValuesCombineNewAndOldTuple(rawAttrValues, newAttrValues,
		    relation, attributeArray, numberOfAttributes)
AttributeValues rawAttrValues;		/* values as stored in 'raw' tuple */
AttributeValues newAttrValues;		/* values of the 'new' tuple */
Relation relation;
AttributeNumberPtr attributeArray;	/* attributes changed by the user */
AttributeNumber numberOfAttributes;	/* size of 'attributeArray' */
{
    AttributeNumber natts;
    AttributeNumber i,j;
    Boolean replacedByRule;
    Boolean replacedByUser;
    char *nulls;
    Datum *values;
    HeapTuple resultTuple;
    long int size;

    /*
     * find the number of attributes of the tuple
     */
    natts = RelationGetNumberOfAttributes(relation);

    /*
     * Now create some arrays needed by 'ModifyHeapTuple()'
     */
    size = natts * sizeof(Datum);
    values = (Datum *) palloc(size);
    if (values == NULL) {
	elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
    }

    size = natts * sizeof(char);
    nulls = (char *) palloc(size);
    if (nulls == NULL) {
	elog(WARN,"attributeValuesCreate: palloc(%ld) failed",size);
    }

    /*
     * Fill these arrays with the right values.
     * For all the attributes of the tuple, check if the
     * attribute has been replaced by the user or by a rule.
     * If yes, then use the value as stored in 'newAttrValues'.
     * Otherwise, use the value as stored in the 'rawAttrValues'.
     */
    for (i=1; i<=natts; i++) {
	replacedByUser = (Boolean) 0;
	for (j=0; j<numberOfAttributes; j++) {
	    if (attributeArray[j] == i) {
		replacedByUser = (Boolean) 1;
		break;
	    }
	}
	replacedByRule = newAttrValues[i-1].isChanged;
	if (replacedByRule || replacedByUser) {
	    values[i-1] = newAttrValues[i-1].value;
	    if (newAttrValues[i-1].isNull) {
		nulls[i-1] = 'n';
	    } else {
		nulls[i-1] = ' ';
	    }
	} else {
	    values[i-1] = rawAttrValues[i-1].value;
	    if (rawAttrValues[i-1].isNull) {
		nulls[i-1] = 'n';
	    } else {
		nulls[i-1] = ' ';
	    }
	}
    }

    /*
     * create the new tuple
     */
    resultTuple = FormHeapTuple(
			RelationGetNumberOfAttributes(relation),
			RelationGetTupleDescriptor(relation),
			values, nulls);
			
    /*
     * free the space occupied by the arrays
     */
    pfree((Pointer)values);
    pfree(nulls);

    /*
     * return the new tuple
     */
    return(resultTuple);
}

@


1.12
log
@Finally I HAVE to use "heap_modifytuple" instead of "heap_formtuple"
because the new tuple must keep the same oid, tid, etc as the old
tuple.
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.11 91/08/14 15:41:27 sp Exp $
d98 1
a98 1
    pfree(a);
d276 1
a276 1
	pfree(replaceValue);
d495 1
a495 1
    pfree(values);
@


1.11
log
@Instead of calling 'ModifyHeapTuple' (which has a bug
tht screws things in 'on retrieve to EMP.salary' do instead nothing'
rules) I now call FormHeapTuple.
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.10 91/04/08 18:14:10 sp Exp Locker: sp $
d270 1
a270 7
	/**---- HACK:
	 **
	 ** The following call to ModifyHeapTuple does not
	 ** do the right thing, so until somebody fixes
	 ** this routine, I'll use 'FormHeapTuple' instead...

	*newTupleP = ModifyHeapTuple( tuple, buffer,
a272 6
	 **
	 **/
    	*newTupleP = FormHeapTuple(
			RelationGetNumberOfAttributes(relation),
			RelationGetTupleDescriptor(relation),
			replaceValue, replaceNull);
@


1.10
log
@misc changes to support locking more than one path in the rule
qualification's tree...
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.9 91/02/08 17:37:48 sp Exp Locker: sp $
d258 6
d270 6
d279 6
@


1.9
log
@Now 'prs2PutLocks' makes the update in place (i.e. it does NOT
create a copy of the tuple).
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.8 91/01/17 19:05:03 sp Exp $
d138 1
a138 1
 * type `LockTypeTupleRetrieveWrite' has been activated and calculated a new
d145 1
a145 1
 * We have to activate all rules that have `LockTypeTupleAppendWrite' locks,
d295 3
a297 1
			    prs2OneLockGetPlanNumber(oneLock));
@


1.8
log
@When 'attributeValuesMakeNewTupl' creates a new tuple, it initializes
it with an empty lock.
,
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.7 91/01/09 19:05:58 sp Exp $
d310 1
a310 2
	t = *newTupleP;
	*newTupleP = prs2PutLocksInTuple(
a312 1
	pfree(t);
d320 1
a320 2
	*newTupleP = prs2PutLocksInTuple(
			    tuple, buffer,
d322 1
@


1.7
log
@'freeDatum' has been renamed to 'datumFree' and takes differnet arguments.
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.6 90/10/16 17:17:43 sp Exp $
d132 4
d154 1
a154 1
 * I thinke that we have the following options, but I don't know
d161 4
d276 1
d318 1
a318 1
	 * this is impossible!!!
d320 1
a320 1
	elog(WARN,"attributeValuesMakeNewTuple: Internal Error");
d327 2
a328 1
	prs2FreeLocks(newLocks);
d331 11
@


1.6
log
@attributeValuesFree now takes an extra argument
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.5 90/09/25 16:42:12 kemnitz Exp $
d84 1
a84 1
     * All these datums are creted via "copyDatum()"
d91 4
a94 1
		freeDatum(a[i-1].value, tdesc->data[i-1]->atttypid);
@


1.5
log
@Updating from revision 1.4 to revision 1.6
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.6 90/08/13 20:29:26 sp Exp $
d59 1
d73 1
a73 1
attributeValuesFree(a)
d75 1
d77 18
@


1.4
log
@New function:
attributeValuesCombineNewAndOldTuple()
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.3 90/03/07 17:16:54 sp Exp $
d15 5
a19 5
#include "c.h"
#include "palloc.h"
#include "log.h"
#include "ftup.h"	/* ModifyHeapTuple() defined here... */
#include "prs2.h"
d111 1
a111 1
 * type `LockTypeRetrieveWrite' has been activated and calculated a new
d118 1
a118 1
 * We have to activate all rules that have `LockTypeAppendWrite' locks,
@


1.3
log
@minor bug fixes
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.2 90/01/31 03:44:58 sp Exp $
d299 155
@


1.2
log
@Prs2Locks has been changed to RuleLocks
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.1 90/01/11 14:42:04 sp Exp Locker: sp $
d41 1
d57 1
a57 1
				&(attrValues[i].isNull));
d59 1
d110 12
a121 10
 * If an attribute that has been checked for rules and has some
 * locks on it of type 'LockTypeWrite', then somehow it must also
 * be marked as so, so that if at a later time (especially when the
 * rule manager is activated at the topmost level of a replace,
 * delete or appaned plan) we want to calculate the value of this
 * attribute we will not check again for all these rules.
 * One way to achieve that is for every attribute with isChecked==true
 * to remove all the corresponding 'LockTypeWrite' locks.
 * Note also that we must keep all the other locks (should they be needed
 * by the rule manager at the tompost level of the plan)...
d145 1
a145 1
			    locks, relation, newTupleP)
d150 1
d167 1
a167 1
    Prs2LockType lockType;
d249 1
a249 1
    locksHaveToChange = false ;
d253 2
a254 2
	lockType = prs2OneLockGetLockType(oneLock);
	if (lockType != LockTypeRetrieveWrite ||
a263 1
	     
d268 8
a281 1
	prs2FreeLocks(newLocks);
a292 1
	prs2FreeLocks(newLocks);
@


1.1
log
@Initial revision
@
text
@d8 1
a8 1
 *$Header: RCS/prs2attr.c,v 1.2 90/01/11 00:22:44 sp Exp Locker: sp $
d145 1
a145 1
Prs2Locks locks;
d159 1
a159 1
    Prs2Locks newLocks;
d249 2
a250 1
	if (lockType != LockTypeWrite || !attrValues[attrNo-1].isCalculated){
@
