From e3a1ab764ef2ce1f331133b456879ae4c186558a Mon Sep 17 00:00:00 2001
From: "Vadim B. Mikheev" <vadim4o@yahoo.com>
Date: Fri, 29 Jan 1999 09:23:17 +0000
Subject: [PATCH] READ COMMITTED isolevel is implemented and is default now.

---
 src/backend/access/heap/heapam.c      |   3 +-
 src/backend/access/nbtree/nbtinsert.c |   3 +-
 src/backend/access/transam/xact.c     |   4 +-
 src/backend/commands/trigger.c        |  23 +-
 src/backend/executor/execMain.c       | 412 ++++++++++++++++++++++----
 src/backend/executor/nodeIndexscan.c  |  44 ++-
 src/backend/executor/nodeSeqscan.c    |  41 ++-
 src/backend/nodes/copyfuncs.c         |  43 +--
 src/backend/parser/gram.c             |   8 +-
 src/backend/utils/time/tqual.c        |   5 +-
 src/include/nodes/execnodes.h         |  38 ++-
 src/include/utils/tqual.h             |   3 +-
 12 files changed, 499 insertions(+), 128 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 8ffd9d41922..bfd57b0cff0 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.39 1998/12/15 12:45:13 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.40 1999/01/29 09:22:51 vadim Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1373,6 +1373,7 @@ l3:
 	if (result != HeapTupleMayBeUpdated)
 	{
 		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated);
+		tuple->t_self = tuple->t_data->t_ctid;
 		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
 		return result;
 	}
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 54ea9ed8abd..01109f564fd 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.32 1998/12/15 12:45:20 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.33 1999/01/29 09:22:52 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,6 +134,7 @@ l1:
 					 * If this tuple is being updated by other transaction
 					 * then we have to wait for its commit/abort.
 					 */
+					ReleaseBuffer(buffer);
 					if (TransactionIdIsValid(xwait))
 					{
 						if (nbuf != InvalidBuffer)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index e6adf442fe6..ec95398131a 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.28 1998/12/18 09:10:18 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.29 1999/01/29 09:22:53 vadim Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -194,7 +194,7 @@ TransactionStateData CurrentTransactionStateData = {
 TransactionState CurrentTransactionState =
 &CurrentTransactionStateData;
 
-int	DefaultXactIsoLevel = XACT_SERIALIZABLE;
+int	DefaultXactIsoLevel = XACT_READ_COMMITTED;
 int	XactIsoLevel;
 
 /* ----------------
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 41bd59ca0d4..a5e27227cf2 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -28,6 +28,7 @@
 #include "utils/inval.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
+#include "executor/executor.h"
 
 #ifndef NO_SECURITY
 #include "miscadmin.h"
@@ -790,6 +791,8 @@ ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple)
 	return;
 }
 
+extern	TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
+
 static HeapTuple
 GetTupleForTrigger(EState *estate, ItemPointer tid, bool before)
 {
@@ -806,6 +809,7 @@ GetTupleForTrigger(EState *estate, ItemPointer tid, bool before)
 		 *	mark tuple for update
 		 */
 		tuple.t_self = *tid;
+ltrmark:;
 		test = heap_mark4update(relation, &tuple, &buffer);
 		switch (test)
 		{
@@ -820,8 +824,23 @@ GetTupleForTrigger(EState *estate, ItemPointer tid, bool before)
 				ReleaseBuffer(buffer);
 				if (XactIsoLevel == XACT_SERIALIZABLE)
 					elog(ERROR, "Can't serialize access due to concurrent update");
-				else
-					elog(ERROR, "Isolation level %u is not supported", XactIsoLevel);
+				else if (!(ItemPointerEquals(&(tuple.t_self), tid)))
+				{
+					TupleTableSlot *slot = EvalPlanQual(estate, 
+						estate->es_result_relation_info->ri_RangeTableIndex, 
+						&(tuple.t_self));
+
+					if (!(TupIsNull(slot)))
+					{
+						*tid = tuple.t_self;
+						goto ltrmark;
+					}
+				}
+				/* 
+				 * if tuple was deleted or PlanQual failed
+				 * for updated tuple - we have not process
+				 * this tuple!
+				 */
 				return(NULL);
 
 			default:
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index a48bbe82bae..7623649a4fa 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.65 1999/01/27 16:48:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.66 1999/01/29 09:22:57 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,10 +64,9 @@ static TupleDesc InitPlan(CmdType operation, Query *parseTree,
 		 Plan *plan, EState *estate);
 static void EndPlan(Plan *plan, EState *estate);
 static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
-			Query *parseTree, CmdType operation,
-			int numberTuples, ScanDirection direction,
-			DestReceiver *destfunc);
-static void ExecRetrieve(TupleTableSlot *slot,
+			CmdType operation, int numberTuples, ScanDirection direction,
+			void (*printfunc) ());
+static void ExecRetrieve(TupleTableSlot *slot, 
 						 DestReceiver *destfunc,
 						 EState *estate);
 static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
@@ -75,7 +74,11 @@ static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
 static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
 		   EState *estate);
 static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
-			EState *estate, Query *parseTree);
+			EState *estate);
+
+TupleTableSlot *EvalPlanQual(EState *estate, Index rti, ItemPointer tid);
+static TupleTableSlot *EvalPlanQualNext(EState *estate);
+
 
 /* end of local decls */
 
@@ -168,11 +171,10 @@ TupleTableSlot *
 ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
 {
 	CmdType		operation;
-	Query	   *parseTree;
 	Plan	   *plan;
 	TupleTableSlot *result;
 	CommandDest dest;
-	DestReceiver   *destfunc;
+	void		(*destination) ();
 
 	/******************
 	 *	sanity checks
@@ -186,42 +188,30 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
 	 ******************
 	 */
 	operation = queryDesc->operation;
-	parseTree = queryDesc->parsetree;
 	plan = queryDesc->plantree;
 	dest = queryDesc->dest;
-	destfunc = DestToFunction(dest);
+	destination = (void (*) ()) DestToFunction(dest);
 	estate->es_processed = 0;
 	estate->es_lastoid = InvalidOid;
 
-	/******************
-	 *	FIXME: the dest setup function ought to be handed the tuple desc
-	 *  for the tuples to be output, but I'm not quite sure how to get that
-	 *  info at this point.  For now, passing NULL is OK because no existing
-	 *  dest setup function actually uses the pointer.
-	 ******************
-	 */
-	(*destfunc->setup) (destfunc, (TupleDesc) NULL);
-
 	switch (feature)
 	{
 
 		case EXEC_RUN:
 			result = ExecutePlan(estate,
 								 plan,
-								 parseTree,
 								 operation,
 								 ALL_TUPLES,
 								 ForwardScanDirection,
-								 destfunc);
+								 destination);
 			break;
 		case EXEC_FOR:
 			result = ExecutePlan(estate,
 								 plan,
-								 parseTree,
 								 operation,
 								 count,
 								 ForwardScanDirection,
-								 destfunc);
+								 destination);
 			break;
 
 			/******************
@@ -231,11 +221,10 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
 		case EXEC_BACK:
 			result = ExecutePlan(estate,
 								 plan,
-								 parseTree,
 								 operation,
 								 count,
 								 BackwardScanDirection,
-								 destfunc);
+								 destination);
 			break;
 
 			/******************
@@ -246,11 +235,10 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
 		case EXEC_RETONE:
 			result = ExecutePlan(estate,
 								 plan,
-								 parseTree,
 								 operation,
 								 ONE_TUPLE,
 								 ForwardScanDirection,
-								 destfunc);
+								 destination);
 			break;
 		default:
 			result = NULL;
@@ -258,8 +246,6 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
 			break;
 	}
 
-	(*destfunc->cleanup) (destfunc);
-
 	return result;
 }
 
@@ -413,9 +399,18 @@ ExecCheckPerms(CmdType operation,
 typedef struct execRowMark
 {
 	Relation	relation;
+	Index		rti;
 	char		resname[32];
 } execRowMark;
 
+typedef struct evalPlanQual
+{
+	Plan				   *plan;
+	Index					rti;
+	EState					estate;
+	struct evalPlanQual	   *free;
+} evalPlanQual;
+
 /* ----------------------------------------------------------------
  *		InitPlan
  *
@@ -426,13 +421,12 @@ typedef struct execRowMark
 static TupleDesc
 InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 {
-	List	   *rangeTable;
-	int			resultRelation;
-	Relation	intoRelationDesc;
-
-	TupleDesc	tupType;
-	List	   *targetList;
-	int			len;
+	List		   *rangeTable;
+	int				resultRelation;
+	Relation		intoRelationDesc;
+	TupleDesc		tupType;
+	List		   *targetList;
+	int				len;
 
 	/******************
 	 *	get information from query descriptor
@@ -537,6 +531,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 				continue;
 			erm = (execRowMark*) palloc(sizeof(execRowMark));
 			erm->relation = relation;
+			erm->rti = rm->rti;
 			sprintf(erm->resname, "ctid%u", rm->rti);
 			estate->es_rowMark = lappend(estate->es_rowMark, erm);
 		}
@@ -669,6 +664,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 
 	estate->es_into_relation_descriptor = intoRelationDesc;
 
+	estate->es_origPlan = plan;
+	estate->es_evalPlanQual = NULL;
+	estate->es_evTuple = NULL;
+	estate->es_useEvalPlan = false;
+
 	return tupType;
 }
 
@@ -753,11 +753,10 @@ EndPlan(Plan *plan, EState *estate)
 static TupleTableSlot *
 ExecutePlan(EState *estate,
 			Plan *plan,
-			Query *parseTree,
 			CmdType operation,
 			int numberTuples,
 			ScanDirection direction,
-			DestReceiver* destfunc)
+			void (*printfunc) ())
 {
 	JunkFilter *junkfilter;
 
@@ -794,7 +793,15 @@ ExecutePlan(EState *estate,
 		 ******************
 		 */
 		/* at the top level, the parent of a plan (2nd arg) is itself */
-		slot = ExecProcNode(plan, plan);
+lnext:;
+		if (estate->es_useEvalPlan)
+		{
+			slot = EvalPlanQualNext(estate);
+			if (TupIsNull(slot))
+				slot = ExecProcNode(plan, plan);
+		}
+		else
+			slot = ExecProcNode(plan, plan);
 
 		/******************
 		 *	if the tuple is null, then we assume
@@ -821,8 +828,6 @@ ExecutePlan(EState *estate,
 		if ((junkfilter = estate->es_junkFilter) != (JunkFilter *) NULL)
 		{
 			Datum		datum;
-
-/*			NameData	attrName; */
 			HeapTuple	newTuple;
 			bool		isNull;
 
@@ -853,8 +858,10 @@ ExecutePlan(EState *estate,
 				execRowMark	   *erm;
 				Buffer			buffer;
 				HeapTupleData	tuple;
+				TupleTableSlot *newSlot;
 				int				test;
 
+lmark:;
 				foreach (l, estate->es_rowMark)
 				{
 					erm = lfirst(l);
@@ -879,10 +886,27 @@ ExecutePlan(EState *estate,
 
 						case HeapTupleUpdated:
 							if (XactIsoLevel == XACT_SERIALIZABLE)
+							{
 								elog(ERROR, "Can't serialize access due to concurrent update");
-							else
-								elog(ERROR, "Isolation level %u is not supported", XactIsoLevel);
-							return(NULL);
+								return(NULL);
+							}
+							else if (!(ItemPointerEquals(&(tuple.t_self), 
+										(ItemPointer)DatumGetPointer(datum))))
+							{
+ 								newSlot = EvalPlanQual(estate, erm->rti, &(tuple.t_self));
+								if (!(TupIsNull(newSlot)))
+								{
+									slot = newSlot;
+									estate->es_useEvalPlan = true;
+									goto lmark;
+								}
+							}
+							/* 
+							 * if tuple was deleted or PlanQual failed
+							 * for updated tuple - we have not return
+							 * this tuple!
+							 */
+							goto lnext;
 
 						default:
 							elog(ERROR, "Unknown status %u from heap_mark4update", test);
@@ -917,7 +941,7 @@ ExecutePlan(EState *estate,
 		{
 			case CMD_SELECT:
 				ExecRetrieve(slot,		/* slot containing tuple */
-							 destfunc,	/* destination's tuple-receiver obj */
+							 printfunc, /* print function */
 							 estate);	/* */
 				result = slot;
 				break;
@@ -933,7 +957,7 @@ ExecutePlan(EState *estate,
 				break;
 
 			case CMD_UPDATE:
-				ExecReplace(slot, tupleid, estate, parseTree);
+				ExecReplace(slot, tupleid, estate);
 				result = NULL;
 				break;
 
@@ -973,7 +997,7 @@ ExecutePlan(EState *estate,
  */
 static void
 ExecRetrieve(TupleTableSlot *slot,
-			 DestReceiver *destfunc,
+			 void (*printfunc) (),
 			 EState *estate)
 {
 	HeapTuple	tuple;
@@ -1000,7 +1024,7 @@ ExecRetrieve(TupleTableSlot *slot,
 	 *	send the tuple to the front end (or the screen)
 	 ******************
 	 */
-	(*destfunc->receiveTuple) (tuple, attrtype, destfunc);
+	(*printfunc) (tuple, attrtype);
 	IncrRetrieved();
 	(estate->es_processed)++;
 }
@@ -1115,7 +1139,8 @@ ExecDelete(TupleTableSlot *slot,
 {
 	RelationInfo	   *resultRelationInfo;
 	Relation			resultRelationDesc;
-	ItemPointerData		ctid;
+	ItemPointerData		ctid,
+						oldtid;
 	int					result;
 
 	/******************
@@ -1140,6 +1165,7 @@ ExecDelete(TupleTableSlot *slot,
 	/*
 	 *	delete the tuple
 	 */
+ldelete:;
 	result = heap_delete(resultRelationDesc, tupleid, &ctid);
 	switch (result)
 	{
@@ -1152,8 +1178,18 @@ ExecDelete(TupleTableSlot *slot,
 		case HeapTupleUpdated:
 			if (XactIsoLevel == XACT_SERIALIZABLE)
 				elog(ERROR, "Can't serialize access due to concurrent update");
-			else
-				elog(ERROR, "Isolation level %u is not supported", XactIsoLevel);
+			else if (!(ItemPointerEquals(tupleid, &ctid)))
+			{
+				TupleTableSlot *slot = EvalPlanQual(estate, 
+						resultRelationInfo->ri_RangeTableIndex, &ctid);
+
+				if (!TupIsNull(slot))
+				{
+					tupleid = &oldtid;
+					*tupleid = ctid;
+					goto ldelete;
+				}
+			}
 			return;
 
 		default:
@@ -1197,13 +1233,13 @@ ExecDelete(TupleTableSlot *slot,
 static void
 ExecReplace(TupleTableSlot *slot,
 			ItemPointer tupleid,
-			EState *estate,
-			Query *parseTree)
+			EState *estate)
 {
 	HeapTuple			tuple;
 	RelationInfo	   *resultRelationInfo;
 	Relation			resultRelationDesc;
-	ItemPointerData		ctid;
+	ItemPointerData		ctid,
+						oldtid;
 	int					result;
 	int					numIndices;
 
@@ -1270,6 +1306,7 @@ ExecReplace(TupleTableSlot *slot,
 	/*
 	 *	replace the heap tuple
 	 */
+lreplace:;
 	result = heap_replace(resultRelationDesc, tupleid, tuple, &ctid);
 	switch (result)
 	{
@@ -1282,8 +1319,18 @@ ExecReplace(TupleTableSlot *slot,
 		case HeapTupleUpdated:
 			if (XactIsoLevel == XACT_SERIALIZABLE)
 				elog(ERROR, "Can't serialize access due to concurrent update");
-			else
-				elog(ERROR, "Isolation level %u is not supported", XactIsoLevel);
+			else if (!(ItemPointerEquals(tupleid, &ctid)))
+			{
+				TupleTableSlot *slot = EvalPlanQual(estate, 
+						resultRelationInfo->ri_RangeTableIndex, &ctid);
+
+				if (!TupIsNull(slot))
+				{
+					tupleid = &oldtid;
+					*tupleid = ctid;
+					goto lreplace;
+				}
+			}
 			return;
 
 		default:
@@ -1480,3 +1527,256 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple)
 
 	return;
 }
+
+TupleTableSlot*
+EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
+{
+	evalPlanQual	   *epq = (evalPlanQual*) estate->es_evalPlanQual;
+	evalPlanQual	   *oldepq;
+	EState			   *epqstate = NULL;
+	Relation			relation;
+	Buffer				buffer;
+	HeapTupleData		tuple;
+	bool				endNode = true;
+
+	Assert(rti != 0);
+
+	if (epq != NULL && epq->rti == 0)
+	{
+		Assert(!(estate->es_useEvalPlan) && 
+				epq->estate.es_evalPlanQual == NULL);
+		epq->rti = rti;
+		endNode = false;
+	}
+
+	/*
+	 * If this is request for another RTE - Ra, - then we have to check
+	 * wasn't PlanQual requested for Ra already and if so then Ra' row 
+	 * was updated again and we have to re-start old execution for Ra 
+	 * and forget all what we done after Ra was suspended. Cool? -:))
+	 */
+	if (epq != NULL && epq->rti != rti && 
+		epq->estate.es_evTuple[rti - 1] != NULL)
+	{
+		do
+		{
+			/* pop previous PlanQual from the stack */
+			epqstate = &(epq->estate);
+			oldepq = (evalPlanQual*) epqstate->es_evalPlanQual;
+			Assert(oldepq->rti != 0);
+			/* stop execution */
+			ExecEndNode(epq->plan, epq->plan);
+			pfree(epqstate->es_evTuple[epq->rti - 1]);
+			epqstate->es_evTuple[epq->rti - 1] = NULL;
+			/* push current PQ to freePQ stack */
+			oldepq->free = epq;
+			epq = oldepq;
+		} while (epq->rti != rti);
+		estate->es_evalPlanQual = (Pointer) epq;
+	}
+
+	/* 
+	 * If we are requested for another RTE then we have to suspend
+	 * execution of current PlanQual and start execution for new one.
+	 */
+	if (epq == NULL || epq->rti != rti)
+	{
+		/* try to reuse plan used previously */
+		evalPlanQual   *newepq = (epq != NULL) ? epq->free : NULL;
+
+		if (newepq == NULL)
+		{
+			newepq = (evalPlanQual*) palloc(sizeof(evalPlanQual));
+			/* Init EState */
+			epqstate = &(newepq->estate);
+			memset(epqstate, 0, sizeof(EState));
+			epqstate->type = T_EState; 
+			epqstate->es_direction = ForwardScanDirection;
+			epqstate->es_snapshot = estate->es_snapshot;
+			epqstate->es_range_table = estate->es_range_table;
+			epqstate->es_param_list_info = estate->es_param_list_info;
+			if (estate->es_origPlan->nParamExec > 0)
+				epqstate->es_param_exec_vals = (ParamExecData *)
+							palloc(estate->es_origPlan->nParamExec * 
+									sizeof(ParamExecData));
+			epqstate->es_tupleTable = 
+				ExecCreateTupleTable(estate->es_tupleTable->size);
+			epqstate->es_refcount = estate->es_refcount;
+			/* ... rest */
+			newepq->plan = copyObject(estate->es_origPlan);
+			newepq->free = NULL;
+			if (epq == NULL)
+			{
+				epqstate->es_evTuple = (HeapTuple*) 
+					palloc(length(estate->es_range_table) * sizeof(HeapTuple));
+				memset(epqstate->es_evTuple, 0, 
+					length(estate->es_range_table) * sizeof(HeapTuple));
+				epqstate->es_evTupleNull = (bool*) 
+					palloc(length(estate->es_range_table) * sizeof(bool));
+				memset(epqstate->es_evTupleNull, false, 
+					length(estate->es_range_table) * sizeof(bool));
+			}
+			else
+			{
+				epqstate->es_evTuple = epq->estate.es_evTuple;
+				epqstate->es_evTupleNull = epq->estate.es_evTupleNull;
+			}
+		}
+		else
+		{
+			epqstate = &(newepq->estate);
+		}
+		/* push current PQ to the stack */
+		epqstate->es_evalPlanQual = (Pointer) epq;
+		estate->es_evalPlanQual = (Pointer) epq = newepq;
+		epq->rti = rti;
+		endNode = false;
+	}
+
+	epqstate = &(epq->estate);
+
+	/*
+	 * Ok - we're requested for the same RTE (-:)).
+	 * I'm not sure about ability to use ExecReScan instead of
+	 * ExecInitNode, so...
+	 */
+	if (endNode)
+		ExecEndNode(epq->plan, epq->plan);
+
+	/* free old RTE' tuple */
+	if (epqstate->es_evTuple[epq->rti - 1] != NULL)
+	{
+		pfree(epqstate->es_evTuple[epq->rti - 1]);
+		epqstate->es_evTuple[epq->rti - 1] = NULL;
+	}
+
+	/* ** fetch tid tuple ** */
+	if (estate->es_result_relation_info != NULL && 
+		estate->es_result_relation_info->ri_RangeTableIndex == rti)
+		relation = estate->es_result_relation_info->ri_RelationDesc;
+	else
+	{
+		List   *l;
+
+		foreach (l, estate->es_rowMark)
+		{
+			if (((execRowMark*) lfirst(l))->rti == rti)
+				break;
+		}
+		relation = ((execRowMark*) lfirst(l))->relation;
+	}
+	tuple.t_self = *tid;
+	for ( ; ; )
+	{
+		heap_fetch(relation, SnapshotDirty, &tuple, &buffer);
+		if (tuple.t_data != NULL)
+		{
+			TransactionId xwait = SnapshotDirty->xmax;
+
+			if (TransactionIdIsValid(SnapshotDirty->xmin))
+				elog(ERROR, "EvalPlanQual: t_xmin is uncommitted ?!");
+			/*
+			 * If tuple is being updated by other transaction then 
+			 * we have to wait for its commit/abort.
+			 */
+			if (TransactionIdIsValid(xwait))
+			{
+				ReleaseBuffer(buffer);
+				XactLockTableWait(xwait);
+				continue;
+			}
+			/*
+			 * Nice! We got tuple - now copy it.
+			 */
+			epqstate->es_evTuple[epq->rti - 1] = heap_copytuple(&tuple);
+			epqstate->es_evTupleNull[epq->rti - 1] = false;
+			ReleaseBuffer(buffer);
+			break;
+		}
+		/*
+		 * Ops! Invalid tuple. Have to check is it updated or deleted.
+		 * Note that it's possible to get invalid SnapshotDirty->tid
+		 * if tuple updated by this transaction. Have we to check this ?
+		 */
+		if (ItemPointerIsValid(&(SnapshotDirty->tid)) && 
+			!(ItemPointerEquals(&(tuple.t_self), &(SnapshotDirty->tid))))
+		{
+			tuple.t_self = SnapshotDirty->tid;	/* updated ... */
+			continue;
+		}
+		/*
+		 * Deleted or updated by this transaction. Do not
+		 * (re-)start execution of this PQ. Continue previous PQ.
+		 */
+		oldepq = (evalPlanQual*) epqstate->es_evalPlanQual;
+		if (oldepq != NULL)
+		{
+			Assert(oldepq->rti != 0);
+			/* push current PQ to freePQ stack */
+			oldepq->free = epq;
+			epq = oldepq;
+			epqstate = &(epq->estate);
+			estate->es_evalPlanQual = (Pointer) epq;
+		}
+		else
+		{									/* this is the first (oldest) PQ
+			epq->rti = 0;					 * - mark as free and 
+			estate->es_useEvalPlan = false;	 * continue Query execution
+			return (NULL);					 */
+		}
+	}
+
+	if (estate->es_origPlan->nParamExec > 0)
+		memset(epqstate->es_param_exec_vals, 0, 
+				estate->es_origPlan->nParamExec * sizeof(ParamExecData));
+	ExecInitNode(epq->plan, epqstate, NULL);
+
+	/*
+	 * For UPDATE/DELETE we have to return tid of actual row
+	 * we're executing PQ for.
+	 */
+	*tid = tuple.t_self;
+
+	return (EvalPlanQualNext(estate));
+}
+
+static TupleTableSlot* 
+EvalPlanQualNext(EState *estate)
+{
+	evalPlanQual	   *epq = (evalPlanQual*) estate->es_evalPlanQual;
+	EState			   *epqstate = &(epq->estate);
+	evalPlanQual	   *oldepq;
+	TupleTableSlot	   *slot;
+
+	Assert(epq->rti != 0);
+
+lpqnext:;
+	slot = ExecProcNode(epq->plan, epq->plan);
+
+	/*
+	 * No more tuples for this PQ. Continue previous one.
+	 */
+	if (TupIsNull(slot))
+	{
+		ExecEndNode(epq->plan, epq->plan);
+		pfree(epqstate->es_evTuple[epq->rti - 1]);
+		epqstate->es_evTuple[epq->rti - 1] = NULL;
+		/* pop old PQ from the stack */
+		oldepq = (evalPlanQual*) epqstate->es_evalPlanQual;
+		if (oldepq == (evalPlanQual*) NULL)
+		{									/* this is the first (oldest) */
+			epq->rti = 0;					/* PQ - mark as free and      */
+			estate->es_useEvalPlan = false;	/* continue Query execution   */
+			return (NULL);
+		}
+		Assert(oldepq->rti != 0);
+		/* push current PQ to freePQ stack */
+		oldepq->free = epq;
+		epq = oldepq;
+		epqstate = &(epq->estate);
+		estate->es_evalPlanQual = (Pointer) epq;
+		goto lpqnext;
+	}
+
+	return (slot);
+}
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index b4a610b7f88..97469e9929b 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.29 1998/11/27 19:52:03 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.30 1999/01/29 09:22:58 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,6 +109,40 @@ IndexNext(IndexScan *node)
 	heapRelation = scanstate->css_currentRelation;
 	numIndices = indexstate->iss_NumIndices;
 	slot = scanstate->css_ScanTupleSlot;
+
+	/*
+	 * Check if we are evaluating PlanQual for tuple of this relation.
+	 * Additional checking is not good, but no other way for now.
+	 * We could introduce new nodes for this case and handle
+	 * IndexScan --> NewNode switching in Init/ReScan plan...
+	 */
+	if (estate->es_evTuple != NULL && 
+		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+	{
+		int		iptr;
+
+		slot->ttc_buffer = InvalidBuffer;
+		slot->ttc_shouldFree = false;
+		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
+		{
+			slot->val = NULL;	/* must not free tuple! */
+			return (slot);
+		}
+		slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
+		for (iptr = 0; iptr < numIndices; iptr++)
+		{
+			scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
+			if (ExecQual(nth(iptr, node->indxqualorig),
+						 scanstate->cstate.cs_ExprContext))
+				break;
+		}
+		if (iptr == numIndices)	/* would not be returned by indices */
+			slot->val = NULL;
+		/* Flag for the next call that no more tuples */
+		estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
+		return (slot);
+	}
+
 	tuple = &(indexstate->iss_htup);
 
 	/* ----------------
@@ -262,6 +296,14 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 	numScanKeys = indexstate->iss_NumScanKeys;
 	indexstate->iss_IndexPtr = 0;
 
+	/* If this is re-scanning of PlanQual ... */
+	if (estate->es_evTuple != NULL && 
+		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+	{
+		estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
+		return;
+	}
+
 	/* it's possible in subselects */
 	if (exprCtxt == NULL)
 		exprCtxt = node->scan.scanstate->cstate.cs_ExprContext;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index a9da3e769d2..b09b94d82a5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.15 1998/09/25 13:38:32 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.16 1999/01/29 09:22:58 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,6 +64,34 @@ SeqNext(SeqScan *node)
 	scanstate = node->scanstate;
 	scandesc = scanstate->css_currentScanDesc;
 	direction = estate->es_direction;
+	slot = scanstate->css_ScanTupleSlot;
+
+	/*
+	 * Check if we are evaluating PlanQual for tuple of this relation.
+	 * Additional checking is not good, but no other way for now.
+	 * We could introduce new nodes for this case and handle
+	 * SeqScan --> NewNode switching in Init/ReScan plan...
+	 */
+	if (estate->es_evTuple != NULL && 
+		estate->es_evTuple[node->scanrelid - 1] != NULL)
+	{
+		slot->ttc_buffer = InvalidBuffer;
+		slot->ttc_shouldFree = false;
+		if (estate->es_evTupleNull[node->scanrelid - 1])
+		{
+			slot->val = NULL;	/* must not free tuple! */
+			return (slot);
+		}
+		slot->val = estate->es_evTuple[node->scanrelid - 1];
+		/*
+		 * Note that unlike IndexScan, SeqScan never use keys
+		 * in heap_beginscan (and this is very bad) - so, here
+		 * we have not check are keys ok or not.
+		 */
+		/* Flag for the next call that no more tuples */
+		estate->es_evTupleNull[node->scanrelid - 1] = true;
+		return (slot);
+	}
 
 	/* ----------------
 	 *	get the next tuple from the access methods
@@ -79,7 +107,6 @@ SeqNext(SeqScan *node)
 	 *	be pfree()'d.
 	 * ----------------
 	 */
-	slot = scanstate->css_ScanTupleSlot;
 
 	slot = ExecStoreTuple(tuple,/* tuple to store */
 						  slot, /* slot to store in */
@@ -374,9 +401,15 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
 		outerPlan = outerPlan((Plan *) node);
 		ExecReScan(outerPlan, exprCtxt, parent);
 	}
-	else
+	else	/* otherwise, we are scanning a relation */
 	{
-		/* otherwise, we are scanning a relation */
+		/* If this is re-scanning of PlanQual ... */
+		if (estate->es_evTuple != NULL && 
+			estate->es_evTuple[node->scanrelid - 1] != NULL)
+		{
+			estate->es_evTupleNull[node->scanrelid - 1] = false;
+			return;
+		}
 		rel = scanstate->css_currentRelation;
 		scan = scanstate->css_currentScanDesc;
 		direction = estate->es_direction;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a008de72be2..7eb2dd34a33 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.55 1999/01/25 18:02:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.56 1999/01/29 09:22:59 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,7 +83,6 @@ CopyPlanFields(Plan *from, Plan *newnode)
 	newnode->plan_size = from->plan_size;
 	newnode->plan_width = from->plan_width;
 	newnode->plan_tupperpage = from->plan_tupperpage;
-	newnode->state = from->state;
 	newnode->targetlist = copyObject(from->targetlist);
 	newnode->qual = copyObject(from->qual);
 	newnode->lefttree = copyObject(from->lefttree);
@@ -138,7 +137,6 @@ _copyResult(Result *from)
 	 * ----------------
 	 */
 	Node_Copy(from, newnode, resconstantqual);
-	Node_Copy(from, newnode, resstate);
 
 	return newnode;
 }
@@ -166,7 +164,6 @@ _copyAppend(Append *from)
 	Node_Copy(from, newnode, unionrtables);
 	newnode->inheritrelid = from->inheritrelid;
 	Node_Copy(from, newnode, inheritrtable);
-	Node_Copy(from, newnode, appendstate);
 
 	return newnode;
 }
@@ -183,7 +180,6 @@ static void
 CopyScanFields(Scan *from, Scan *newnode)
 {
 	newnode->scanrelid = from->scanrelid;
-	Node_Copy(from, newnode, scanstate);
 	return;
 }
 
@@ -248,7 +244,6 @@ _copyIndexScan(IndexScan *from)
 	newnode->indxid = listCopy(from->indxid);
 	Node_Copy(from, newnode, indxqual);
 	Node_Copy(from, newnode, indxqualorig);
-	Node_Copy(from, newnode, indxstate);
 
 	return newnode;
 }
@@ -304,12 +299,6 @@ _copyNestLoop(NestLoop *from)
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 	CopyJoinFields((Join *) from, (Join *) newnode);
 
-	/* ----------------
-	 *	copy remainder of node
-	 * ----------------
-	 */
-	Node_Copy(from, newnode, nlstate);
-
 	return newnode;
 }
 
@@ -346,8 +335,6 @@ _copyMergeJoin(MergeJoin *from)
 	newnode->mergeleftorder[0] = from->mergeleftorder[0];
 	newnode->mergeleftorder[1] = 0;
 
-	Node_Copy(from, newnode, mergestate);
-
 	return newnode;
 }
 
@@ -375,12 +362,9 @@ _copyHashJoin(HashJoin *from)
 
 	newnode->hashjoinop = from->hashjoinop;
 
-	Node_Copy(from, newnode, hashjoinstate);
-
-	newnode->hashjointable = from->hashjointable;
+	/* both are unused !.. */
 	newnode->hashjointablekey = from->hashjointablekey;
 	newnode->hashjointablesize = from->hashjointablesize;
-	newnode->hashdone = from->hashdone;
 
 	return newnode;
 }
@@ -437,12 +421,6 @@ _copyMaterial(Material *from)
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 	CopyTempFields((Temp *) from, (Temp *) newnode);
 
-	/* ----------------
-	 *	copy remainder of node
-	 * ----------------
-	 */
-	Node_Copy(from, newnode, matstate);
-
 	return newnode;
 }
 
@@ -463,14 +441,6 @@ _copySort(Sort *from)
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 	CopyTempFields((Temp *) from, (Temp *) newnode);
 
-	/* ----------------
-	 *	copy remainder of node
-	 * ----------------
-	 */
-	Node_Copy(from, newnode, sortstate);
-	Node_Copy(from, newnode, psortstate);
-	newnode->cleaned = from->cleaned;
-
 	return newnode;
 }
 
@@ -490,7 +460,6 @@ _copyGroup(Group *from)
 	newnode->numCols = from->numCols;
 	newnode->grpColIdx = palloc(from->numCols * sizeof(AttrNumber));
 	memcpy(newnode->grpColIdx, from->grpColIdx, from->numCols * sizeof(AttrNumber));
-	Node_Copy(from, newnode, grpstate);
 
 	return newnode;
 }
@@ -507,7 +476,6 @@ _copyAgg(Agg *from)
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 
 	newnode->aggs = get_agg_tlist_references(newnode);
-	Node_Copy(from, newnode, aggstate);
 
 	return newnode;
 }
@@ -553,7 +521,6 @@ _copyUnique(Unique *from)
 	else
 		newnode->uniqueAttr = NULL;
 	newnode->uniqueAttrNum = from->uniqueAttrNum;
-	Node_Copy(from, newnode, uniquestate);
 
 	return newnode;
 }
@@ -579,9 +546,8 @@ _copyHash(Hash *from)
 	 * ----------------
 	 */
 	Node_Copy(from, newnode, hashkey);
-	Node_Copy(from, newnode, hashstate);
 
-	newnode->hashtable = from->hashtable;
+	/* both are unused !.. */
 	newnode->hashtablekey = from->hashtablekey;
 	newnode->hashtablesize = from->hashtablesize;
 
@@ -599,7 +565,6 @@ _copySubPlan(SubPlan *from)
 	newnode->setParam = listCopy(from->setParam);
 	newnode->parParam = listCopy(from->parParam);
 	Node_Copy(from, newnode, sublink);
-	newnode->shutdown = from->shutdown;
 
 	return newnode;
 }
@@ -1901,7 +1866,7 @@ copyObject(void *from)
 			}
 			break;
 		default:
-			elog(NOTICE, "copyObject: don't know how to copy %d", nodeTag(from));
+			elog(ERROR, "copyObject: don't know how to copy %d", nodeTag(from));
 			retval = from;
 			break;
 	}
diff --git a/src/backend/parser/gram.c b/src/backend/parser/gram.c
index 186b0213e5c..30d6011cc44 100644
--- a/src/backend/parser/gram.c
+++ b/src/backend/parser/gram.c
@@ -239,7 +239,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/Attic/gram.c,v 2.64 1999/01/26 23:32:04 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/Attic/gram.c,v 2.65 1999/01/29 09:23:02 vadim Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -4791,7 +4791,7 @@ static const short yycheck[] = {     3,
     -1,    -1,    -1,    -1,    -1,    -1,    -1,   214
 };
 /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
-#line 3 "/usr/local/bison/bison.simple"
+#line 3 "/usr/share/misc/bison.simple"
 
 /* Skeleton output parser for bison,
    Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
@@ -4984,7 +4984,7 @@ __yy_memcpy (char *to, char *from, int count)
 #endif
 #endif
 
-#line 196 "/usr/local/bison/bison.simple"
+#line 196 "/usr/share/misc/bison.simple"
 
 /* The user can define YYPARSE_PARAM as the name of an argument to be passed
    into yyparse.  The argument should have type void *.
@@ -11088,7 +11088,7 @@ case 960:
     break;}
 }
    /* the action file gets copied in in place of this dollarsign */
-#line 498 "/usr/local/bison/bison.simple"
+#line 498 "/usr/share/misc/bison.simple"
 
   yyvsp -= yylen;
   yyssp -= yylen;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index ecef85787c3..360027b1eb4 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.23 1998/12/18 09:10:39 vadim Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.24 1999/01/29 09:23:12 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -368,6 +368,7 @@ bool
 HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 {
 	SnapshotDirty->xmin = SnapshotDirty->xmax = InvalidTransactionId;
+	ItemPointerSetInvalid(&(SnapshotDirty->tid));
 
 	if (AMI_OVERRIDE)
 		return true;
@@ -413,6 +414,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 	{
 		if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 			return true;
+		SnapshotDirty->tid = tuple->t_ctid;
 		return false;							/* updated by other */
 	}
 
@@ -437,6 +439,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
 		return true;
 
+	SnapshotDirty->tid = tuple->t_ctid;
 	return false;								/* updated by other */
 }
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 37de50b0147..d9cdd2509de 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.21 1999/01/25 12:01:19 vadim Exp $
+ * $Id: execnodes.h,v 1.22 1999/01/29 09:23:13 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,21 +194,27 @@ typedef struct JunkFilter
  */
 typedef struct EState
 {
-	NodeTag		type;
-	ScanDirection es_direction;
-	Snapshot	es_snapshot;
-	List	   *es_range_table;
-	RelationInfo *es_result_relation_info;
-	Relation	es_into_relation_descriptor;
-	ParamListInfo es_param_list_info;
-	ParamExecData *es_param_exec_vals;	/* this is for subselects */
-	int			es_BaseId;
-	TupleTable	es_tupleTable;
-	JunkFilter *es_junkFilter;
-	int		   *es_refcount;
-	uint32		es_processed;	/* # of tuples processed */
-	Oid			es_lastoid;		/* last oid processed (by INSERT) */
-	List	   *es_rowMark;		/* not good place, but there is no other */
+	NodeTag			type;
+	ScanDirection	es_direction;
+	Snapshot		es_snapshot;
+	List		   *es_range_table;
+	RelationInfo   *es_result_relation_info;
+	Relation		es_into_relation_descriptor;
+	ParamListInfo	es_param_list_info;
+	ParamExecData  *es_param_exec_vals;	/* this is for subselects */
+	int				es_BaseId;
+	TupleTable		es_tupleTable;
+	JunkFilter	   *es_junkFilter;
+	int			   *es_refcount;
+	uint32			es_processed;	/* # of tuples processed */
+	Oid				es_lastoid;		/* last oid processed (by INSERT) */
+	List		   *es_rowMark;		/* not good place, but there is no other */
+	/* Below is to re-evaluate plan qual in READ COMMITTED mode */
+	struct Plan	   *es_origPlan;
+	Pointer			es_evalPlanQual;
+	bool		   *es_evTupleNull;
+	HeapTuple	   *es_evTuple;
+	bool			es_useEvalPlan;
 } EState;
 
 /* ----------------
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 17ff6f3d90f..7f811388e22 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tqual.h,v 1.18 1998/12/18 09:09:55 vadim Exp $
+ * $Id: tqual.h,v 1.19 1999/01/29 09:23:17 vadim Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@ typedef struct SnapshotData
 	TransactionId	xmax;			/* XID >= xmax are invisible to me */
 	uint32			xcnt;			/* # of xact below */
 	TransactionId  *xip;			/* array of xacts in progress */
+	ItemPointerData	tid;			/* required for Dirty snapshot -:( */
 }			SnapshotData;
 
 typedef SnapshotData *Snapshot;
-- 
GitLab