Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 9 Aug 2005 18:39:22 -0300 (BRT)
From:      Marcos Tischer Vallim <tischer@ig.com.br>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   ports/84718: [PATCH] databases/postgresql74-server: Add option from query hierarchy "CONNECT BY"
Message-ID:  <200508092139.j79LdMQH001774@loadbalancer.widesoft.com.br>
Resent-Message-ID: <200508092050.j79KoMHu083917@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         84718
>Category:       ports
>Synopsis:       [PATCH] databases/postgresql74-server: Add option from query hierarchy "CONNECT BY"
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Aug 09 20:50:20 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Marcos Tischer Vallim
>Release:        FreeBSD 5.4-RELEASE-p6 i386
>Organization:
>Environment:
System: FreeBSD loadbalancer.widesoft.com.br 5.4-RELEASE-p6 FreeBSD 5.4-RELEASE-p6 #0: Wed Jul 27 19:20:55 BRT 2005 root@loadball.widesoft.com.br:/usr/obj/usr/src/sys/GENERIC i386


	
>Description:
	Add a patch wrote by Evgen Potemkin, which allows PgSQL to make hierarchical queries a la Oracle do.

	This patch is optional and selected with one more OPTION

	Port maintainer (girgen@FreeBSD.org) is cc'd.
>How-To-Repeat:
	
>Fix:

	

--- patch-postgres74-server begins here ---
diff -ruN --exclude=CVS /root/postgresql74-server/Makefile ./Makefile
--- /root/postgresql74-server/Makefile	Tue May 10 20:42:53 2005
+++ ./Makefile	Wed Aug  3 16:25:09 2005
@@ -86,6 +86,12 @@
 #  to run regression tests:
 OPTIONS+=	TESTS "Allows the use of a \"check\" target (server)" off
 OPTIONS+=	DEBUG "Builds with debugging symbols" off
+OPTIONS+=	HIER "Builds with query hierarchy" off
+
+.  if defined(WITH_HIER)
+EXTRA_PATCHES=	${FILESDIR}/hier-Pg7.4-0.5.3.diff
+USE_BISON=yes
+.  endif
 
 .  if defined(SERVER_ONLY) && defined(WITH_PAM)
 CONFIGURE_ARGS+=--with-pam
diff -ruN --exclude=CVS /root/postgresql74-server/files/hier-Pg7.4-0.5.3.diff ./files/hier-Pg7.4-0.5.3.diff
--- /root/postgresql74-server/files/hier-Pg7.4-0.5.3.diff	Wed Dec 31 21:00:00 1969
+++ ./files/hier-Pg7.4-0.5.3.diff	Wed Aug  3 16:25:25 2005
@@ -0,0 +1,4136 @@
+diff -Prdc --exclude-from=exclude src/backend/commands/explain.c src/backend/commands/explain.c
+*** src/backend/commands/explain.c	Fri Oct 17 01:14:26 2003
+--- src/backend/commands/explain.c	Mon Jul  5 08:19:37 2004
+***************
+*** 474,479 ****
+--- 474,482 ----
+  		case T_Hash:
+  			pname = "Hash";
+  			break;
++  		case T_Conn:
++  			pname = "Hier";
++  			break;
+  		default:
+  			pname = "???";
+  			break;
+diff -Prdc --exclude-from=exclude src/backend/executor/Makefile src/backend/executor/Makefile
+*** src/backend/executor/Makefile	Thu Mar 27 16:51:27 2003
+--- src/backend/executor/Makefile	Mon Jul  5 08:19:37 2004
+***************
+*** 18,24 ****
+         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
+         nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
+         nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+!        nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
+  
+  all: SUBSYS.o
+  
+--- 18,25 ----
+         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
+         nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
+         nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+!        nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o \
+!        nodeConn.o
+  
+  all: SUBSYS.o
+  
+diff -Prdc --exclude-from=exclude src/backend/executor/execAmi.c src/backend/executor/execAmi.c
+*** src/backend/executor/execAmi.c	Tue Mar  2 18:56:28 2004
+--- src/backend/executor/execAmi.c	Mon Jul  5 09:21:16 2004
+***************
+*** 37,42 ****
+--- 37,43 ----
+  #include "executor/nodeSubqueryscan.h"
+  #include "executor/nodeTidscan.h"
+  #include "executor/nodeUnique.h"
++ #include "executor/nodeConn.h"
+  
+  
+  /*
+***************
+*** 171,176 ****
+--- 172,181 ----
+  			ExecReScanLimit((LimitState *) node, exprCtxt);
+  			break;
+  
++  		case T_ConnectState:
++  			ExecReScanConn((ConnectState *) node, exprCtxt);
++  			break;
++ 
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			break;
+***************
+*** 217,222 ****
+--- 222,231 ----
+  			ExecSortMarkPos((SortState *) node);
+  			break;
+  
++  		case T_ConnectState:
++  			ExecConnMarkPos((ConnectState *) node);
++  			break;
++  
+  		default:
+  			/* don't make hard error unless caller asks to restore... */
+  			elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
+***************
+*** 258,263 ****
+--- 267,276 ----
+  			ExecSortRestrPos((SortState *) node);
+  			break;
+  
++     case T_ConnectState:
++       ExecConnRestrPos((ConnectState *) node);
++       break;
++                         
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			break;
+diff -Prdc --exclude-from=exclude src/backend/executor/execProcnode.c src/backend/executor/execProcnode.c
+*** src/backend/executor/execProcnode.c	Fri Aug  8 21:41:39 2003
+--- src/backend/executor/execProcnode.c	Mon Jul  5 08:19:37 2004
+***************
+*** 100,105 ****
+--- 100,106 ----
+  #include "executor/nodeUnique.h"
+  #include "miscadmin.h"
+  #include "tcop/tcopprot.h"
++ #include "executor/nodeConn.h"
+  
+  /* ------------------------------------------------------------------------
+   *		ExecInitNode
+***************
+*** 213,218 ****
+--- 214,223 ----
+  			result = (PlanState *) ExecInitLimit((Limit *) node, estate);
+  			break;
+  
++  		case T_Conn:
++  			result = ExecInitConn((Conn *) node, estate);
++  			break;
++  
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			result = NULL;		/* keep compiler quiet */
+***************
+*** 372,377 ****
+--- 377,386 ----
+  			result = ExecLimit((LimitState *) node);
+  			break;
+  
++  		case T_ConnectState:
++  			result = ExecConn((ConnectState *) node);
++  			break;
++  
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			result = NULL;
+***************
+*** 464,469 ****
+--- 473,481 ----
+  		case T_Limit:
+  			return ExecCountSlotsLimit((Limit *) node);
+  
++  		case T_Conn:
++  			return ExecCountSlotsConn((Conn *) node);
++  
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			break;
+***************
+*** 592,597 ****
+--- 604,613 ----
+  			ExecEndLimit((LimitState *) node);
+  			break;
+  
++  		case T_ConnectState:
++  			ExecEndConn((ConnectState *) node);
++  			break;
++  
+  		default:
+  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+  			break;
+diff -Prdc --exclude-from=exclude src/backend/executor/execQual.c src/backend/executor/execQual.c
+*** src/backend/executor/execQual.c	Thu Dec 18 22:23:54 2003
+--- src/backend/executor/execQual.c	Mon Jul  5 08:19:37 2004
+***************
+*** 57,62 ****
+--- 57,63 ----
+  				 ExprContext *econtext,
+  				 bool *isNull, ExprDoneCond *isDone);
+  static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
++ static Datum ExecEvalFakeVar(FakeVar *variable, ExprContext *econtext, bool *isNull);
+  static Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+  			  bool *isNull);
+  static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
+***************
+*** 403,408 ****
+--- 404,410 ----
+  	 * We assume it's OK to point to the existing tupleDescriptor, rather
+  	 * than copy that too.
+  	 */
++ 	
+  	if (attnum == InvalidAttrNumber)
+  	{
+  		MemoryContext oldContext;
+***************
+*** 417,422 ****
+--- 419,480 ----
+  		MemoryContextSwitchTo(oldContext);
+  		return PointerGetDatum(tempSlot);
+  	}
++ 	result = heap_getattr(heapTuple,	/* tuple containing attribute */
++ 						  attnum,		/* attribute number of desired
++ 										 * attribute */
++ 						  tuple_type,	/* tuple descriptor of tuple */
++ 						  isNull);		/* return: is attribute null? */
++ 
++ 	return result;
++ }
++ 
++ /* Same as ExecEvalVar but returns only value from current tuple, or
++  * (Datum) ) if tuple slot if undefined.
++  */
++ static Datum
++ ExecEvalFakeVar(FakeVar *variable, ExprContext *econtext, bool *isNull)
++ {
++ 	Datum		result;
++ 	TupleTableSlot *slot;
++ 	AttrNumber	attnum;
++ 	HeapTuple	heapTuple;
++ 	TupleDesc	tuple_type;
++ 	/*
++ 	 * get the slot we want
++ 	 */
++ 	/* FakeVars can't be OUTER or INNER so get the tuple only from 
++ 	 * the relation being scanned.
++ 	 */
++ 	slot = econtext->ecxt_scantuple;
++ 
++ 	/* if slot is undefined, then we're on layer under Hier where FakeVar not really exist,
++ 	 * so show our fake nature, and return 0.
++ 	 */
++ 	if(!slot){
++ 		if(variable->vartype != INT4OID)
++ 			elog(ERROR,"ExecEvalFakeVar: FakeVars with type other than INT4 is not supported (internal error)");
++ 		return Int32GetDatum(0);
++ 	}
++ 	/*
++ 	 * extract tuple information from the slot
++ 	 */
++ 	heapTuple = slot->val;
++ 	tuple_type = slot->ttc_tupleDescriptor;
++ 
++ 	attnum = variable->varattno;
++ 
++ 	/* (See prolog of ExecEvalVar for explanation of this Assert) */
++ 		
++ 	if(attnum - 1 >= tuple_type->natts ){
++ 	  return Int32GetDatum(0);
++ 	}
++ 	Assert(attnum <= 0 ||
++ 		   (attnum - 1 <= tuple_type->natts - 1 &&
++ 			tuple_type->attrs[attnum - 1] != NULL &&
++ 		  variable->vartype == tuple_type->attrs[attnum - 1]->atttypid));
++ 
++ 	if (attnum == InvalidAttrNumber)
++ 		elog(ERROR,"ExecEvalFakeVar: invalid attnum %u (internal error)",attnum);
+  
+  	result = heap_getattr(heapTuple,	/* tuple containing attribute */
+  						  attnum,		/* attribute number of desired
+***************
+*** 2221,2232 ****
+--- 2279,2299 ----
+  	 * here we dispatch the work to the appropriate type of function given
+  	 * the type of our expression.
+  	 */
++   
+  	expr = expression->expr;
+  	switch (nodeTag(expr))
+  	{
+  		case T_Var:
+  			retDatum = ExecEvalVar((Var *) expr, econtext, isNull);
+  			break;
++ 		case T_FakeVar:
++ 			retDatum = ExecEvalFakeVar((FakeVar *) expr, econtext, isNull);
++ 		        break;
++ 		case T_Prior:
++  			*isNull = true;
++  			retDatum = (Datum)0;
++  			break;
++ 
+  		case T_Const:
+  			{
+  				Const	   *con = (Const *) expr;
+***************
+*** 2421,2427 ****
+--- 2488,2496 ----
+  	switch (nodeTag(node))
+  	{
+  		case T_Var:
++     case T_FakeVar:
+  		case T_Const:
++ 		case T_Prior:
+  		case T_Param:
+  		case T_CoerceToDomainValue:
+  			/* No special setup needed for these node types */
+***************
+*** 3134,3143 ****
+  							  projInfo->pi_tupNulls,
+  							  projInfo->pi_itemIsDone,
+  							  isDone);
+- 
+  	/*
+  	 * store the tuple in the projection slot and return the slot.
+  	 */
+  	return ExecStoreTuple(newTuple,		/* tuple to store */
+  						  slot, /* slot to store in */
+  						  InvalidBuffer,		/* tuple has no buffer */
+--- 3203,3212 ----
+  							  projInfo->pi_tupNulls,
+  							  projInfo->pi_itemIsDone,
+  							  isDone);
+  	/*
+  	 * store the tuple in the projection slot and return the slot.
+  	 */
++ 
+  	return ExecStoreTuple(newTuple,		/* tuple to store */
+  						  slot, /* slot to store in */
+  						  InvalidBuffer,		/* tuple has no buffer */
+diff -Prdc --exclude-from=exclude src/backend/executor/nodeConn.c src/backend/executor/nodeConn.c
+*** src/backend/executor/nodeConn.c	Thu Jan  1 00:00:00 1970
+--- src/backend/executor/nodeConn.c	Mon Jul  5 08:19:37 2004
+***************
+*** 0 ****
+--- 1,323 ----
++ /*------------------------------------------------------------------------- *
++  * nodeConn.c
++  *	  Routines to handle connecting of relations.
++  *
++  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
++  * Portions Copyright (c) 1994, Regents of the University of California
++  *
++  * Based on nodeSort.c, by Evgen Potemkin evgent@terminal.ru, 11.2002
++  *
++  *-------------------------------------------------------------------------
++  */
++ 
++ #include "postgres.h"
++ 
++ #include "executor/execdebug.h"
++ #include "executor/nodeConn.h"
++ #include "utils/tupleconn.h"
++ #include "miscadmin.h"
++ #include "utils/memutils.h"
++ 
++ #include "nodes/print.h"
++ 
++ /* ----------------------------------------------------------------
++  *		ExecConn
++  *
++  *		Connects tuples from the outer subtree of the node using tupleconn,
++  *		which saves the results in a temporary file or memory. After the
++  *		initial call, returns a tuple from the file with each call.
++  *
++  *		Conditions:
++  *		  -- none.
++  *
++  *		Initial States:
++  *		  -- the outer child is prepared to return the first tuple.
++  * ----------------------------------------------------------------
++  */
++ TupleTableSlot *
++ ExecConn(ConnectState *node)
++ {
++ 	EState	   *estate;
++ 	TupleTableSlot *slot;
++ 	HeapTuple	heapTuple;
++ 	ScanDirection dir;
++ 	Tupleconnstate *tupleconnstate;
++ 	bool		should_free;
++ 	List		*squal;
++ 	ExprContext *econtext;
++ 	int			head;
++ 	bool		ok;
++   Conn *plan=(Conn *) node->ss.ps.plan;
++ 	/*
++ 	 * get state info from node
++ 	 */
++ 	estate = node->ss.ps.state;
++ 	dir = estate->es_direction;
++ 	tupleconnstate = (Tupleconnstate *) node->tupleconnstate;
++ 	
++   econtext = node->ss.ps.ps_ExprContext;
++ 	if (!node->conn_Done)
++ 	{
++ 		TupleDesc	tupDesc;
++     PlanState *outerNode;
++     
++   	SO1_printf("ExecConn: %s\n",
++ 				   "connecting subplan");
++ 
++ 		/*
++ 		 * Want to scan subplan in the forward direction while creating
++ 		 * the data for processing.
++ 		 */
++ 
++ 		/*
++ 		 * Initialize tupleconn module.
++ 		 */
++ 		SO1_printf("ExecConn: %s\n",
++ 				   "calling tupleconn_begin");
++ 
++ 		outerNode = outerPlanState((PlanState *) node);
++ 		tupDesc = ExecGetResultType(outerNode);
++ 
++     tupleconnstate = tupleconn_begin_heap(SortMem,tupDesc,plan->connOp,
++          plan->priorsList,plan->levelColNum,((Plan *)plan)->plan_rows,plan->useHash);
++ 		node->tupleconnstate = (void *) tupleconnstate;
++ 
++ 		/*
++ 		 * Scan the subplan and feed all the tuples to tupleconn.
++ 		 */
++     squal = makeList1(node->startQual);
++ 
++ 		ResetExprContext(econtext);
++ 
++ 		for (;;)
++ 		{
++ 			slot = ExecProcNode(outerNode);
++ 			if (TupIsNull(slot))
++ 				break;
++ 			econtext->ecxt_scantuple = slot;
++ 			/* check if tuple is a head of tree */
++ 			head = ExecQual(squal, econtext, false);
++ 			/* store it */
++ 			tupleconn_puttuple(tupleconnstate, (void *) slot->val,head);
++ 		}
++ 
++ 		tupleconn_donestoring(tupleconnstate);
++ 		/* connect them.
++ 		 * parentExpr, childExpr, econtext is not needed in tupleconn,
++ 		 * so pass them only for connecting stage.
++ 		 */
++ 		tupleconn_performconn(tupleconnstate,plan->parentExpr,plan->childExpr,econtext);
++ 		/*
++ 		 * finally set the connected flag to true
++ 		 */
++ 		node->conn_Done = true;
++ 		SO1_printf(stderr, "ExecConn: connecting done.\n");
++ 	}
++ 	SO1_printf("ExecConn: %s\n",
++ 			   "retrieving tuple from tupleconn");
++ 
++ 	/*
++ 	 * Get the first or next tuple from tupleconn. Returns NULL if no more
++ 	 * tuples.
++ 	 */
++   squal = node->ss.ps.qual;
++ 	ok=0;	
++ 	/* everywhere here slot is the same pointer, so get it here */
++ 	slot = node->ss.ps.ps_ResultTupleSlot;
++ 	/* Apply HAVING clause to post-qual tuples */
++ 	do{
++ 		/* if qual present, clear context */
++ 		if(squal) ResetExprContext(econtext);
++ 		heapTuple = tupleconn_getheaptuple(tupleconnstate,
++ 									   &should_free);
++ 		ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
++ 		if(!squal) break; /* don't qual */
++ 		if(!heapTuple) break; /* no more tuples */
++ 		econtext->ecxt_scantuple = slot;
++ 		/* if tuple satisfy qual, pass it, else get next */
++ 		ok=ExecQual(squal, econtext, false);
++ 	}while(!ok);
++ 
++ 	if(squal) ResetExprContext(econtext);
++ 	return slot;
++ }
++ 
++ /* Everything below is same as nodeSort.c
++  */
++ /* ----------------------------------------------------------------
++  *		ExecInitConn
++  *
++  *		Creates the run-time state information for the conn node
++  *		produced by the planner and initailizes its outer subtree.
++  * ----------------------------------------------------------------
++  */
++ ConnectState *
++ ExecInitConn(Conn *node, EState *estate)
++ {
++ 	ConnectState *connstate;
++ 	
++ 	SO1_printf("ExecInitConn: %s\n",
++ 			   "initializing conn node");
++ 	/*
++ 	 * assign the node's execution state
++ 	 */
++ 
++ 	/*
++ 	 * create state structure
++ 	 */
++ 	connstate = makeNode(ConnectState);
++ 	connstate->ss.ps.plan = (Plan *) node;
++ 	connstate->ss.ps.qual = ExecInitExpr( (Expr *)((Plan *)node)->qual, (PlanState *)connstate);;
++ 	connstate->ss.ps.state = estate;
++ 
++ 	connstate->startQual = ExecInitExpr( (Expr *)node->startQual, (PlanState *)connstate);
++ 	connstate->parentExpr = ExecInitExpr((Expr *)node->parentExpr,(PlanState *)connstate);
++ 	connstate->childExpr = ExecInitExpr( (Expr *)node->childExpr, (PlanState *)connstate);
++ 
++   connstate->conn_Done = false;
++ 	connstate->tupleconnstate = NULL;
++ 
++ 	/*
++ 	 * Miscellaneous initialization
++ 	 */
++   ExecAssignExprContext(estate, &connstate->ss.ps); 
++ #define CONN_NSLOTS 2
++ 
++ 	/*
++ 	 * tuple table initialization
++ 	 */
++ 	ExecInitResultTupleSlot(estate, &connstate->ss.ps);
++ 	ExecInitScanTupleSlot(estate, &connstate->ss);
++ 	/*
++ 	 * initializes child nodes
++ 	 */
++   outerPlanState(connstate) = ExecInitNode(outerPlan(node), estate);
++ 	/*
++ 	 * initialize tuple type.  no need to initialize projection info
++ 	 * because this node doesn't do projections.
++ 	 */
++ 	ExecAssignResultTypeFromOuterPlan(&connstate->ss.ps);
++ 	ExecAssignScanTypeFromOuterPlan(&connstate->ss);
++ 	connstate->ss.ps.ps_ProjInfo = NULL;
++ 
++ 	SO1_printf("ExecInitConn: %s\n",
++ 			   "conn node initialized");
++ 
++ 	return connstate;
++ }
++ 
++ int
++ ExecCountSlotsConn(Conn *node)
++ {
++ 	return ExecCountSlotsNode(outerPlan((Plan *) node)) +
++ 		ExecCountSlotsNode(innerPlan((Plan *) node))+CONN_NSLOTS;
++ }
++ 
++ /* ----------------------------------------------------------------
++  *		ExecEndConn(node)
++  * ----------------------------------------------------------------
++  */
++ void
++ ExecEndConn(ConnectState *node)
++ {
++ 	/*
++ 	 * get info from the conn state
++ 	 */
++ 	SO1_printf("ExecEndConn: %s\n",
++ 			   "shutting down conn node");
++ 
++ 	/*
++ 	 * shut down the subplan
++ 	 */
++ 
++ 	/*
++ 	 * clean out the tuple table
++ 	 */
++ 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
++ 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
++ 
++ 	/*
++ 	 * Release tupleconn resources
++ 	 */
++ 	if (node->tupleconnstate != NULL)
++ 		tupleconn_end((Tupleconnstate *) node->tupleconnstate);
++ 	node->tupleconnstate = NULL;
++ 
++ 	ExecEndNode(outerPlanState(node));
++ 
++ 	SO1_printf("ExecEndConn: %s\n",
++ 			   "conn node shutdown");
++ }
++ 
++ /* ----------------------------------------------------------------
++  *		ExecConnMarkPos
++  *
++  *		Calls tupleconn to save the current position in the processed file.
++  * ----------------------------------------------------------------
++  */
++ void
++ ExecConnMarkPos(ConnectState *node)
++ {
++ 
++ 	/*
++ 	 * if we haven't processed yet, just return
++ 	 */
++ 	if (!node->conn_Done)
++ 		return;
++ 
++ 	tupleconn_markpos((Tupleconnstate *) node->tupleconnstate);
++ }
++ 
++ /* ----------------------------------------------------------------
++  *		ExecConnRestrPos
++  *
++  *		Calls tupleconn to restore the last saved processed file position.
++  * ----------------------------------------------------------------
++  */
++ void
++ ExecConnRestrPos(ConnectState *node)
++ {
++ 
++ 	/*
++ 	 * if we haven't processed yet, just return.
++ 	 */
++ 	if (!node->conn_Done)
++ 		return;
++ 
++ 	/*
++ 	 * restore the scan to the previously marked position
++ 	 */
++ 	tupleconn_restorepos((Tupleconnstate *) node->tupleconnstate);
++ }
++ 
++ void
++ ExecReScanConn(ConnectState *node, ExprContext *exprCtxt)
++ {
++ 
++ 	/*
++ 	 * If we haven't processed yet, just return. If outerplan' chgParam is
++ 	 * not NULL then it will be re-scanned by ExecProcNode, else - no
++ 	 * reason to re-scan it at all.
++ 	 */
++ 	if (!node->conn_Done)
++ 		return;
++ 
++ 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
++ 
++ 	/*
++ 	 * If subnode is to be rescanned then we forget previous process results;
++ 	 * we have to re-read the subplan and re-connect.
++ 	 *
++ 	 * Otherwise we can just rewind and rescan the processed output.
++ 	 */
++ 
++ 	if (((PlanState *) node)->lefttree->chgParam != NULL)
++ 	{
++ 		node->conn_Done = false;
++ 		tupleconn_end((Tupleconnstate *) node->tupleconnstate);
++ 		node->tupleconnstate = NULL;
++ 	}
++ 	else
++ 		tupleconn_rescan((Tupleconnstate *) node->tupleconnstate);
++ 
++ }
+diff -Prdc --exclude-from=exclude src/backend/nodes/copyfuncs.c src/backend/nodes/copyfuncs.c
+*** src/backend/nodes/copyfuncs.c	Sun Aug 17 23:43:25 2003
+--- src/backend/nodes/copyfuncs.c	Mon Jul  5 08:19:37 2004
+***************
+*** 455,460 ****
+--- 455,494 ----
+  	return newnode;
+  }
+  
++ /* ----------------
++  *		_copyConn
++  * ----------------
++  */
++ static Conn *
++ _copyConn(Conn *from)
++ {
++ 	List *tl,*ttl;
++ 	Conn	   *newnode = makeNode(Conn);
++ 	
++ 	/*
++  	 * copy node superclass fields
++  	 */
++  	CopyPlanFields((Plan *) from, (Plan *) newnode);
++  	
++  	COPY_NODE_FIELD(startQual);
++ 
++ 	COPY_NODE_FIELD(parentExpr);
++  	COPY_NODE_FIELD(childExpr);
++  	
++  	COPY_SCALAR_FIELD(connOp);
++ 
++  	COPY_SCALAR_FIELD(levelColNum);
++  	COPY_SCALAR_FIELD(useHash);
++ 
++ 	foreach(tl,from->priorsList){
++ 	    ttl = lfirst(tl);
++ 	    newnode->priorsList = lappend(newnode->priorsList,
++ 		 makeListi2(lfirsti(ttl),lfirsti(lnext(ttl))));
++ 	}
++ 	
++  	return newnode;
++ }
++  
+  
+  /*
+   * _copyGroup
+***************
+*** 663,668 ****
+--- 697,727 ----
+  	return newnode;
+  }
+  
++ static FakeVar *
++ _copyFakeVar(FakeVar *from)
++ {
++   FakeVar            *newnode = makeNode(FakeVar);
++ 
++   COPY_SCALAR_FIELD(varattno);
++   COPY_SCALAR_FIELD(vartype);
++   COPY_SCALAR_FIELD(vartypmod);
++ 
++   return newnode;
++ }
++ 
++ static Prior *
++ _copyPrior(Prior *from)
++ {
++ 	Prior		   *newnode = makeNode(Prior);
++ 
++ 	/*
++ 	 * copy remainder of node
++ 	 */
++ 	COPY_SCALAR_FIELD(numcol);
++ 
++ 	return newnode;
++ }
++ 
+  /*
+   * _copyConst
+   */
+***************
+*** 1249,1254 ****
+--- 1308,1339 ----
+  	return newnode;
+  }
+  
++ /* see _equalHierClause */
++ static HierClause *
++ _copyHierClause(HierClause *from)
++ {
++ 	List *tl,*ttl;
++  	HierClause *newnode = makeNode(HierClause);
++ 	
++  	COPY_NODE_FIELD(startQual);
++  	
++  	COPY_NODE_FIELD(parentExpr);
++ 	COPY_NODE_FIELD(childExpr);
++ 	
++  	COPY_SCALAR_FIELD(connOp);
++  
++  	COPY_SCALAR_FIELD(levelColNum);
++  	COPY_SCALAR_FIELD(priorsListNItems);
++  	COPY_SCALAR_FIELD(useHash);
++ 	foreach(tl,from->priorsList){
++ 	    ttl = lfirst(tl);
++ 	    newnode->priorsList = lappend(newnode->priorsList,
++ 		makeListi2(lfirsti(ttl),lfirsti(lnext(ttl))));
++ 	}
++ 
++  	return newnode;
++ }
++  
+  static SortClause *
+  _copySortClause(SortClause *from)
+  {
+***************
+*** 1382,1387 ****
+--- 1467,1473 ----
+  	COPY_STRING_FIELD(name);
+  	COPY_NODE_FIELD(indirection);
+  	COPY_NODE_FIELD(val);
++ 	COPY_SCALAR_FIELD(prior);
+  
+  	return newnode;
+  }
+***************
+*** 1526,1531 ****
+--- 1612,1620 ----
+  	COPY_NODE_FIELD(sortClause);
+  	COPY_NODE_FIELD(limitOffset);
+  	COPY_NODE_FIELD(limitCount);
++ 
++ 	COPY_NODE_FIELD(hierClause);
++ 
+  	COPY_NODE_FIELD(setOperations);
+  	COPY_INTLIST_FIELD(resultRelations);
+  	COPY_NODE_FIELD(in_info_list);
+***************
+*** 2534,2539 ****
+--- 2623,2634 ----
+  		case T_Limit:
+  			retval = _copyLimit(from);
+  			break;
++  		case T_Conn:
++  			retval = _copyConn(from);
++  			break;
++  		case T_HierClause:
++  			retval = _copyHierClause(from);
++  			break;
+  
+  			/*
+  			 * PRIMITIVE NODES
+***************
+*** 2550,2555 ****
+--- 2645,2656 ----
+  		case T_Var:
+  			retval = _copyVar(from);
+  			break;
++ 		case T_FakeVar:
++ 			retval = _copyFakeVar(from);
++ 			break;
++ 		case T_Prior:
++ 			retval = _copyPrior(from);
++ 			break;
+  		case T_Const:
+  			retval = _copyConst(from);
+  			break;
+diff -Prdc --exclude-from=exclude src/backend/nodes/equalfuncs.c src/backend/nodes/equalfuncs.c
+*** src/backend/nodes/equalfuncs.c	Sun Aug 17 23:43:26 2003
+--- src/backend/nodes/equalfuncs.c	Mon Jul  5 08:19:37 2004
+***************
+*** 155,160 ****
+--- 155,178 ----
+  }
+  
+  static bool
++ _equalFakeVar(FakeVar *a, FakeVar *b)
++ {
++   COMPARE_SCALAR_FIELD(varattno);
++   COMPARE_SCALAR_FIELD(vartype);
++   COMPARE_SCALAR_FIELD(vartypmod);
++ 
++   return true;
++ }
++ 
++ static bool
++ _equalPrior(Prior *a, Prior *b)
++ {
++ 	COMPARE_SCALAR_FIELD(numcol);
++ 
++ 	return true;
++ }
++   
++ static bool
+  _equalConst(Const *a, Const *b)
+  {
+  	COMPARE_SCALAR_FIELD(consttype);
+***************
+*** 622,627 ****
+--- 640,648 ----
+  	COMPARE_NODE_FIELD(sortClause);
+  	COMPARE_NODE_FIELD(limitOffset);
+  	COMPARE_NODE_FIELD(limitCount);
++ 
++ 	COMPARE_NODE_FIELD(hierClause);
++ 
+  	COMPARE_NODE_FIELD(setOperations);
+  	COMPARE_INTLIST_FIELD(resultRelations);
+  	COMPARE_NODE_FIELD(in_info_list);
+***************
+*** 637,642 ****
+--- 658,684 ----
+  }
+  
+  static bool
++ _equalHierClause(HierClause *a, HierClause *b)
++ {
++ 	if (!equal(a->startQual, b->startQual))
++ 		return false;
++ 	if (!equal(a->parentExpr, b->parentExpr))
++ 		return false;
++ 	if (!equal(a->childExpr, b->childExpr))
++ 		return false;
++ 	/* we can skip comparing the connOpName because in the DB
++ 	 * can't exist at the same time several operators with
++ 	 * same name and parameters, it's checked by PG.
++ 	 */
++ 	COMPARE_SCALAR_FIELD(connOp);
++ 	COMPARE_SCALAR_FIELD(levelColNum);
++ 	COMPARE_SCALAR_FIELD(priorsListNItems);
++ 	COMPARE_SCALAR_FIELD(useHash);
++ 	
++ 	return true;
++ }
++ 
++ static bool
+  _equalInsertStmt(InsertStmt *a, InsertStmt *b)
+  {
+  	COMPARE_NODE_FIELD(relation);
+***************
+*** 1674,1679 ****
+--- 1716,1727 ----
+  		case T_Var:
+  			retval = _equalVar(a, b);
+  			break;
++ 		case T_FakeVar:
++ 			retval = _equalFakeVar(a, b);
++ 			break;
++ 		case T_Prior:
++ 			retval = _equalPrior(a, b);
++ 			break;
+  		case T_Const:
+  			retval = _equalConst(a, b);
+  			break;
+***************
+*** 2089,2095 ****
+  		case T_FuncWithArgs:
+  			retval = _equalFuncWithArgs(a, b);
+  			break;
+! 
+  		default:
+  			elog(ERROR, "unrecognized node type: %d",
+  				 (int) nodeTag(a));
+--- 2137,2146 ----
+  		case T_FuncWithArgs:
+  			retval = _equalFuncWithArgs(a, b);
+  			break;
+!     case T_HierClause:
+!       retval = _equalHierClause(a, b);
+!       break;
+!                          
+  		default:
+  			elog(ERROR, "unrecognized node type: %d",
+  				 (int) nodeTag(a));
+diff -Prdc --exclude-from=exclude src/backend/nodes/outfuncs.c src/backend/nodes/outfuncs.c
+*** src/backend/nodes/outfuncs.c	Sun Aug 17 23:43:26 2003
+--- src/backend/nodes/outfuncs.c	Mon Jul  5 08:19:37 2004
+***************
+*** 575,580 ****
+--- 575,600 ----
+  	WRITE_INT_FIELD(varoattno);
+  }
+  
++ /*
++  *    FakeVar is a subclass of Expr
++  */
++ static void
++ _outFakeVar(StringInfo str, FakeVar *node)
++ {
++ 	WRITE_NODE_TYPE("FAKEVAR");
++ 
++ 	WRITE_INT_FIELD(varattno);
++ 	WRITE_OID_FIELD(vartype);
++ 	WRITE_INT_FIELD(vartypmod);
++ }
++ static void
++ _outPrior(StringInfo str, Prior *node)
++ {
++ 	appendStringInfo(str,
++ 				" PRIOR :numcol %d ", node->numcol);
++ }
++ 
++ 
+  static void
+  _outConst(StringInfo str, Const *node)
+  {
+***************
+*** 1256,1261 ****
+--- 1276,1282 ----
+  	WRITE_NODE_FIELD(sortClause);
+  	WRITE_NODE_FIELD(limitOffset);
+  	WRITE_NODE_FIELD(limitCount);
++ 	WRITE_NODE_FIELD(hierClause);
+  	WRITE_NODE_FIELD(setOperations);
+  	WRITE_INTLIST_FIELD(resultRelations);
+  
+***************
+*** 1263,1268 ****
+--- 1284,1317 ----
+  }
+  
+  static void
++ _outHierClause(StringInfo str, HierClause *node)
++ {
++ 	List *tl;
++ 	
++  	appendStringInfo(str, " HIERCLAUSE :startQual ");
++  	_outNode(str, node->startQual);
++ 	
++  	appendStringInfo(str, " :parentExpr ");
++  	_outNode(str, node->parentExpr);
++  	
++  	appendStringInfo(str, " :childExpr ");
++  	_outNode(str, node->childExpr);
++  	
++ 	appendStringInfo(str, " :priorsListNItemsCount %d ",node->priorsListNItems);
++ 
++ 	appendStringInfo(str, " :levelColNum %d ",node->levelColNum);
++ 
++  	appendStringInfo(str, " :priorsList ");
++ 	foreach(tl, node->priorsList){
++ 	    _outIntList(str,lfirst(tl));
++ 	}  
++   
++  	appendStringInfo(str, " :connOp %u ",node->connOp);
++ 
++  	appendStringInfo(str, " :useHash %s ",booltostr(node->useHash));
++ }
++  
++ static void
+  _outSortClause(StringInfo str, SortClause *node)
+  {
+  	WRITE_NODE_TYPE("SORTCLAUSE");
+***************
+*** 1625,1630 ****
+--- 1674,1685 ----
+  			case T_Var:
+  				_outVar(str, obj);
+  				break;
++ 			case T_FakeVar:
++ 				_outFakeVar(str, obj);
++ 				break;
++  			case T_Prior:
++  				_outPrior(str, obj);
++  				break;
+  			case T_Const:
+  				_outConst(str, obj);
+  				break;
+***************
+*** 1816,1821 ****
+--- 1871,1879 ----
+  			case T_FuncCall:
+  				_outFuncCall(str, obj);
+  				break;
++  			case T_HierClause:
++  				_outHierClause(str, obj);
++  				break;
+  
+  			default:
+  
+diff -Prdc --exclude-from=exclude src/backend/nodes/readfuncs.c src/backend/nodes/readfuncs.c
+*** src/backend/nodes/readfuncs.c	Sun Aug 17 23:43:26 2003
+--- src/backend/nodes/readfuncs.c	Mon Jul  5 08:19:37 2004
+***************
+*** 211,216 ****
+--- 211,217 ----
+  	READ_NODE_FIELD(sortClause);
+  	READ_NODE_FIELD(limitOffset);
+  	READ_NODE_FIELD(limitCount);
++ 	READ_NODE_FIELD(hierClause);
+  	READ_NODE_FIELD(setOperations);
+  	READ_INTLIST_FIELD(resultRelations);
+  
+***************
+*** 219,224 ****
+--- 220,253 ----
+  	READ_DONE();
+  }
+  
++ /* ----------------
++  *		_readHierClause
++  * ----------------
++  */
++ static HierClause *
++ _readHierClause(void)
++ {
++   int i;
++   READ_LOCALS(HierClause);
++   
++   READ_NODE_FIELD(startQual);
++   READ_NODE_FIELD(parentExpr);
++   READ_NODE_FIELD(childExpr);
++   
++   
++   READ_UINT_FIELD(priorsListNItems);
++   READ_UINT_FIELD(levelColNum);
++   token = pg_strtok(&length); /* skip :priorsList */
++   for(i=0;i<local_node->priorsListNItems;i++){
++     local_node->priorsList = lappend(local_node->priorsList,toIntList(nodeRead(true)));
++   }
++ 
++   READ_OID_FIELD(connOp);
++   READ_BOOL_FIELD(useHash);
++   
++   READ_DONE();
++ }
++ 
+  /*
+   * _readNotifyStmt
+   */
+***************
+*** 364,369 ****
+--- 393,423 ----
+  	READ_DONE();
+  }
+  
++ 
++ /*
++  *    _readFakeVar
++  */
++ static FakeVar *
++ _readFakeVar(void)
++ {
++   READ_LOCALS(FakeVar);
++ 
++ 	READ_INT_FIELD(varattno);
++ 	READ_OID_FIELD(vartype);
++ 	READ_INT_FIELD(vartypmod);
++ 
++   READ_DONE();
++ }
++ static Prior *
++ _readPrior(void)
++ {
++     READ_LOCALS(Prior);
++ 
++     READ_INT_FIELD(numcol);
++     READ_DONE();
++ }
++ 
++ 
+  /*
+   * _readConst
+   */
+***************
+*** 983,988 ****
+--- 1037,1044 ----
+  		return_value = _readRangeVar();
+  	else if (MATCH("VAR", 3))
+  		return_value = _readVar();
++ 	else if (MATCH("FAKEVAR", 7))
++ 		return_value = _readFakeVar();
+  	else if (MATCH("CONST", 5))
+  		return_value = _readConst();
+  	else if (MATCH("PARAM", 5))
+***************
+*** 1049,1054 ****
+--- 1105,1114 ----
+  		return_value = _readNotifyStmt();
+  	else if (MATCH("DECLARECURSOR", 13))
+  		return_value = _readDeclareCursorStmt();
++ 	else if (MATCH("HIERCLAUSE", 10))
++ 		return_value = _readHierClause();
++  	else if (MATCH("PRIOR", 5))
++  		return_value = _readPrior();
+  	else
+  	{
+  		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
+diff -Prdc --exclude-from=exclude src/backend/optimizer/path/costsize.c src/backend/optimizer/path/costsize.c
+*** src/backend/optimizer/path/costsize.c	Tue Apr  6 18:46:25 2004
+--- src/backend/optimizer/path/costsize.c	Mon Jul  5 08:19:37 2004
+***************
+*** 479,484 ****
+--- 479,549 ----
+  	path->total_cost = startup_cost + run_cost;
+  }
+  
++ void
++ cost_conn(Path *path, Query *root,
++ 		  List *pathkeys, double tuples, int width)
++ {
++ 	Cost		startup_cost = 0;
++ 	Cost		run_cost = 0;	
++ 	double		nbytes = relation_byte_size(tuples, width);
++ 	long		sortmembytes = SortMem * 1024L;
++ 	double	x,cmpamnt,i,j;
++ 
++ 	/*
++ 	 * We want to be sure the cost of a sort is never estimated as zero,
++  	 * even if passed-in tuple count is zero.  Besides, mustn't do
++  	 * log(0)...
++  	 */
++  	if (tuples < 2.0)
++  		tuples = 2.0;
++  
++  	x = log(tuples)/log(log(tuples));
++  	x = floor(x); //number of runs, slightly overestimated
++ 	
++  	cmpamnt=0.0;
++  	for(i=1;i<x;i++)
++  		for(j=0;j<x-i-1;j++)
++  			cmpamnt+=pow(x,x-j);
++  	cmpamnt=floor(cmpamnt/2);// rought amount of compares = half of worst case
++  	if (cmpamnt < 1.0)
++  		cmpamnt=1.0;
++  	/*
++  	 * CPU costs
++  	 *
++ 	 * assume: cost of comparation + cost of evaluating parent and child expr ==
++ 	 *           3 * cost of comparation 
++ 	 * cost is:	3 * cost of comparation *
++ 	 *            ( amount of compares + check every tuple for being top node )
++ 	 * if HAVING clause present then add cost of qualification of every tuple.
++ 	 * assume we're connected all of input tuples, in real this may be not a true, so
++ 	 * we're estimating the worst case.
++  	 */
++  	startup_cost += 3 * cpu_operator_cost * ( cmpamnt + tuples );
++  	if(root->havingQual){
++  		startup_cost += cpu_operator_cost * tuples;
++  	}
++  
++  	/* disk costs */
++  	if (nbytes > sortmembytes)
++  	{
++  		double		npages = ceil(nbytes / BLCKSZ);
++  		double		npageaccesses;
++  
++  		npageaccesses = 2.0 * npages * x;
++  		/* Assume half are sequential (cost 1), half are not */
++  		startup_cost += npageaccesses *
++  			(1.0 + cost_nonsequential_access(npages)) * 0.5;
++  	}
++  
++  	/*
++  	 * Note: should we bother to assign a nonzero run_cost to reflect the
++  	 * overhead of extracting tuples from the sort result?	Probably not
++  	 * worth worrying about.
++  	 */
++  	path->startup_cost = startup_cost;
++  	path->total_cost = startup_cost + run_cost;
++ }
++  
+  /*
+   * cost_sort
+   *	  Determines and returns the cost of sorting a relation, including
+diff -Prdc --exclude-from=exclude src/backend/optimizer/plan/createplan.c src/backend/optimizer/plan/createplan.c
+*** src/backend/optimizer/plan/createplan.c	Sun Feb 29 17:36:48 2004
+--- src/backend/optimizer/plan/createplan.c	Mon Jul  5 08:19:37 2004
+***************
+*** 1595,1600 ****
+--- 1595,1637 ----
+  	return node;
+  }
+  
++ /* called in same terms as make_sort.
++  * copy fileds from HierClause to newly created Conn node,
++  * and calc costs 
++  */
++ Conn *
++ make_conn(Query *root,
++ 			 List *tlist,
++ 			 Plan *lefttree)
++ {
++ 	Conn    *node = makeNode(Conn);
++  	Plan	   *plan = &node->plan;
++  	HierClause *hier = (HierClause *)root->hierClause;
++  	Path		conn_path;		/* dummy for result of cost_conn */
++  
++   copy_plan_costsize(plan, lefttree); /* only care about copying size */
++ // 	cost_conn(&conn_path, root, NIL,
++ // 			  lefttree->plan_rows, lefttree->plan_width);
++  	plan->startup_cost = conn_path.startup_cost + lefttree->startup_cost;
++  	plan->total_cost = conn_path.total_cost + lefttree->total_cost;
++  
++  	plan->plan_rows = lefttree->plan_rows;
++  	plan->plan_width = lefttree->plan_width;
++  	plan->targetlist = tlist;
++  	plan->qual = (List *)root->havingQual;
++  	plan->lefttree = lefttree;
++  	plan->righttree = NULL;
++  	node->startQual = hier->startQual;
++ 	node->parentExpr = hier->parentExpr;
++ 	node->levelColNum = hier->levelColNum;
++ 		
++ 	node->childExpr = hier->childExpr;
++  	node->connOp = hier->connOp;
++ 	node->priorsList = hier->priorsList;
++ 	node->useHash = hier->useHash;
++  	return node;
++ }
++ 
+  Append *
+  make_append(List *appendplans, bool isTarget, List *tlist)
+  {
+diff -Prdc --exclude-from=exclude src/backend/optimizer/plan/planner.c src/backend/optimizer/plan/planner.c
+*** src/backend/optimizer/plan/planner.c	Tue May 11 02:21:55 2004
+--- src/backend/optimizer/plan/planner.c	Mon Jul  5 08:19:37 2004
+***************
+*** 49,54 ****
+--- 49,55 ----
+  #define EXPRKIND_RTFUNC 2
+  #define EXPRKIND_LIMIT	3
+  #define EXPRKIND_ININFO 4
++ #define EXPRKIND_STARTWITH 5
+  
+  
+  static Node *preprocess_expression(Query *parse, Node *expr, int kind);
+***************
+*** 131,138 ****
+  	}
+  
+  	/* executor wants to know total number of Params used overall */
+! 	result_plan->nParamExec = length(PlannerParamList);
+! 
+  	/* final cleanup of the plan */
+  	set_plan_references(result_plan, parse->rtable);
+  
+--- 132,138 ----
+  	}
+  
+  	/* executor wants to know total number of Params used overall */
+!  	result_plan->nParamExec = length(PlannerParamList);
+  	/* final cleanup of the plan */
+  	set_plan_references(result_plan, parse->rtable);
+  
+***************
+*** 242,248 ****
+--- 242,256 ----
+  	parse->in_info_list = (List *)
+  		preprocess_expression(parse, (Node *) parse->in_info_list,
+  							  EXPRKIND_ININFO);
++  	if(parse->hierClause){
++  		((HierClause *)parse->hierClause)->startQual = preprocess_expression(parse, ((HierClause *)parse->hierClause)->startQual,
++  											  EXPRKIND_STARTWITH);
+  
++  		((HierClause *)parse->hierClause)->parentExpr = preprocess_expression(parse, ((HierClause *)parse->hierClause)->parentExpr,
++  											  EXPRKIND_STARTWITH);
++  		((HierClause *)parse->hierClause)->childExpr = preprocess_expression(parse, ((HierClause *)parse->hierClause)->childExpr,
++  											  EXPRKIND_STARTWITH);		
++  	}
+  	/* Also need to preprocess expressions for function RTEs */
+  	foreach(lst, parse->rtable)
+  	{
+***************
+*** 270,287 ****
+  	 * implicitly-ANDed-list form at this point, even though they are
+  	 * declared as Node *.
+  	 */
+! 	newHaving = NIL;
+! 	foreach(lst, (List *) parse->havingQual)
+! 	{
+! 		Node	   *havingclause = (Node *) lfirst(lst);
+  
+! 		if (contain_agg_clause(havingclause))
+! 			newHaving = lappend(newHaving, havingclause);
+! 		else
+! 			parse->jointree->quals = (Node *)
+! 				lappend((List *) parse->jointree->quals, havingclause);
+! 	}
+! 	parse->havingQual = (Node *) newHaving;
+  
+  	/*
+  	 * If we have any outer joins, try to reduce them to plain inner
+--- 278,297 ----
+  	 * implicitly-ANDed-list form at this point, even though they are
+  	 * declared as Node *.
+  	 */
+! 	if(!parse->hierClause){
+! 		newHaving = NIL;
+! 		foreach(lst, (List *) parse->havingQual)
+! 		{
+! 			Node	   *havingclause = (Node *) lfirst(lst);
+  
+! 			if (contain_agg_clause(havingclause))
+! 				newHaving = lappend(newHaving, havingclause);
+! 			else
+! 				parse->jointree->quals = (Node *)
+! 					lappend((List *) parse->jointree->quals, havingclause);
+! 		}
+! 		parse->havingQual = (Node *) newHaving;
+!  	}
+  
+  	/*
+  	 * If we have any outer joins, try to reduce them to plain inner
+***************
+*** 565,571 ****
+  	List	   *current_pathkeys;
+  	List	   *sort_pathkeys;
+  
+! 	if (parse->setOperations)
+  	{
+  		/*
+  		 * Construct the plan for set operations.  The result will not
+--- 575,581 ----
+  	List	   *current_pathkeys;
+  	List	   *sort_pathkeys;
+  
+!   if (parse->setOperations)
+  	{
+  		/*
+  		 * Construct the plan for set operations.  The result will not
+***************
+*** 1291,1297 ****
+  												 result_plan->plan_rows);
+  		}
+  	}
+! 
+  	/*
+  	 * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
+  	 */
+--- 1301,1313 ----
+  												 result_plan->plan_rows);
+  		}
+  	}
+!  	/*
+!  	 * If there is a CONNECT BY clause, add the CONN node
+!  	 */
+!  	if (parse->hierClause)
+!  	{
+!  		result_plan = (Plan *) make_conn(parse, tlist, result_plan);
+!  	}
+  	/*
+  	 * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.
+  	 */
+diff -Prdc --exclude-from=exclude src/backend/optimizer/plan/setrefs.c src/backend/optimizer/plan/setrefs.c
+*** src/backend/optimizer/plan/setrefs.c	Tue May 11 13:15:23 2004
+--- src/backend/optimizer/plan/setrefs.c	Mon Jul  5 08:19:37 2004
+***************
+*** 22,27 ****
+--- 22,28 ----
+  #include "optimizer/var.h"
+  #include "parser/parsetree.h"
+  #include "utils/lsyscache.h"
++ #include "catalog/pg_type.h"
+  
+  
+  typedef struct
+***************
+*** 185,190 ****
+--- 186,201 ----
+  			fix_expr_references(plan,
+  								(Node *) ((Hash *) plan)->hashkeys);
+  			break;
++  		case T_Conn:
++  			set_uppernode_references(plan, (Index) 0);			
++ 
++  			fix_expr_references(plan, (Node *) plan->targetlist);
++  			fix_expr_references(plan, (Node *) plan->qual);
++  			fix_expr_references(plan, (Node *)((Conn *) plan)->startQual);
++ 
++  			fix_expr_references(plan, (Node *)((Conn *) plan)->parentExpr);
++  			fix_expr_references(plan, (Node *)((Conn *) plan)->childExpr);
++  			break;
+  		case T_Material:
+  		case T_Sort:
+  		case T_Unique:
+***************
+*** 490,495 ****
+--- 501,524 ----
+  									   subvarno,
+  									   subplan_targetlist,
+  									   tlist_has_non_vars);
++  	if(IsA( plan, Conn)){
++  		((Conn *)plan)->startQual = 
++  			replace_vars_with_subplan_refs((Node *) ((Conn *)plan)->startQual,
++  									   subvarno,
++  									   subplan_targetlist,
++  									   tlist_has_non_vars);
++ 
++  		((Conn *)plan)->parentExpr =
++  			replace_vars_with_subplan_refs((Node *) ((Conn *)plan)->parentExpr,
++  									   subvarno,
++  									   subplan_targetlist,
++  									   tlist_has_non_vars);
++  		((Conn *)plan)->childExpr = 
++  			replace_vars_with_subplan_refs((Node *) ((Conn *)plan)->childExpr,
++  									   subvarno,
++  									   subplan_targetlist,
++  									   tlist_has_non_vars);
++  	}
+  }
+  
+  /*
+***************
+*** 564,570 ****
+  {
+  	if (node == NULL)
+  		return NULL;
+! 	if (IsA(node, Var))
+  	{
+  		Var		   *var = (Var *) node;
+  		Resdom	   *resdom;
+--- 593,608 ----
+  {
+  	if (node == NULL)
+  		return NULL;
+! 	if (IsA(node, FakeVar))
+! 	{
+! 		/* FakeVar isn't present on subplan so just copy it*/
+! 		FakeVar		   *var = (FakeVar *) node;
+! 		FakeVar		   *newvar;
+! 
+! 		newvar = (FakeVar *) copyObject((Node *)var);
+! 		return (Node *) newvar;
+! 	}
+! 	else if (IsA(node, Var))
+  	{
+  		Var		   *var = (Var *) node;
+  		Resdom	   *resdom;
+***************
+*** 682,688 ****
+  {
+  	if (node == NULL)
+  		return NULL;
+! 	if (IsA(node, Var))
+  	{
+  		Var		   *var = (Var *) node;
+  		Resdom	   *resdom;
+--- 720,735 ----
+  {
+  	if (node == NULL)
+  		return NULL;
+! 	if (IsA(node, FakeVar))
+! 	{
+! 		/* FakeVar isn't present on subplan so just copy it*/
+! 		FakeVar		   *var = (FakeVar *) node;
+! 		FakeVar		   *newvar;
+! 
+! 		newvar = (FakeVar *) copyObject((Node *)var);
+! 		return (Node *) newvar;
+! 	}
+! 	else if (IsA(node, Var))
+  	{
+  		Var		   *var = (Var *) node;
+  		Resdom	   *resdom;
+diff -Prdc --exclude-from=exclude src/backend/optimizer/plan/subselect.c src/backend/optimizer/plan/subselect.c
+*** src/backend/optimizer/plan/subselect.c	Tue May 11 13:15:23 2004
+--- src/backend/optimizer/plan/subselect.c	Mon Jul  5 08:19:37 2004
+***************
+*** 444,449 ****
+--- 444,450 ----
+  				case T_Material:
+  				case T_FunctionScan:
+  				case T_Sort:
++ 				case T_Conn:
+  
+  					/*
+  					 * Don't add another Material node if there's one
+***************
+*** 1037,1042 ****
+--- 1038,1044 ----
+  		case T_Unique:
+  		case T_SetOp:
+  		case T_Group:
++ 		case T_Conn:
+  			break;
+  
+  		default:
+diff -Prdc --exclude-from=exclude src/backend/optimizer/prep/prepjointree.c src/backend/optimizer/prep/prepjointree.c
+*** src/backend/optimizer/prep/prepjointree.c	Sat Jan 10 00:30:39 2004
+--- src/backend/optimizer/prep/prepjointree.c	Mon Jul  5 08:19:37 2004
+***************
+*** 257,262 ****
+--- 257,275 ----
+  				ResolveNew((Node *) parse->in_info_list,
+  						   varno, 0, subtlist, CMD_SELECT, 0);
+  
++  			if(parse->hierClause){
++  				((HierClause *)parse->hierClause)->startQual = 
++  						ResolveNew(((HierClause *)parse->hierClause)->startQual,
++  								   varno, 0, subtlist, CMD_SELECT, 0);
++ 
++  				((HierClause *)parse->hierClause)->parentExpr = 
++  						ResolveNew(((HierClause *)parse->hierClause)->parentExpr,
++  								   varno, 0, subtlist, CMD_SELECT, 0);
++  				((HierClause *)parse->hierClause)->childExpr = 
++  						ResolveNew(((HierClause *)parse->hierClause)->childExpr,
++  								   varno, 0, subtlist, CMD_SELECT, 0);
++  			}
++ 
+  			foreach(rt, parse->rtable)
+  			{
+  				RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+***************
+*** 408,413 ****
+--- 421,427 ----
+  		subquery->groupClause ||
+  		subquery->havingQual ||
+  		subquery->sortClause ||
++  		subquery->hierClause ||
+  		subquery->distinctClause ||
+  		subquery->limitOffset ||
+  		subquery->limitCount)
+diff -Prdc --exclude-from=exclude src/backend/optimizer/util/clauses.c src/backend/optimizer/util/clauses.c
+*** src/backend/optimizer/util/clauses.c	Wed Jan 28 00:05:25 2004
+--- src/backend/optimizer/util/clauses.c	Mon Jul  5 08:19:37 2004
+***************
+*** 2226,2231 ****
+--- 2226,2232 ----
+  	switch (nodeTag(node))
+  	{
+  		case T_Var:
++ 		case T_FakeVar:
+  		case T_Const:
+  		case T_Param:
+  		case T_CoerceToDomainValue:
+***************
+*** 2233,2238 ****
+--- 2234,2244 ----
+  		case T_RangeTblRef:
+  			/* primitive node types with no subnodes */
+  			break;
++ 		case T_Prior:
++ 			{
++ 				return walker(((Prior *) node)->val, context);
++ 			}
++ 			break;
+  		case T_Aggref:
+  			return walker(((Aggref *) node)->target, context);
+  		case T_ArrayRef:
+***************
+*** 2432,2437 ****
+--- 2438,2454 ----
+  					 errmsg("relation reference \"%s\" cannot be used in an expression",
+  							((RangeVar *) node)->relname)));
+  			break;
++  		case T_HierClause:
++  			{
++  				HierClause *hier = (HierClause *)node;
++  				if (walker(hier->startQual, context))
++  					return true;
++  				if (walker(hier->parentExpr, context))
++  					return true;
++  				if (walker(hier->childExpr, context))
++  					return true;
++  			}
++  			break;
+  		default:
+  			elog(ERROR, "unrecognized node type: %d",
+  				 (int) nodeTag(node));
+***************
+*** 2479,2484 ****
+--- 2496,2504 ----
+  		return true;
+  	if (walker(query->in_info_list, context))
+  		return true;
++  	if (walker(query->hierClause, context))
++  		return true;
++  	
+  	foreach(rt, query->rtable)
+  	{
+  		RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
+***************
+*** 2599,2604 ****
+--- 2619,2625 ----
+  	switch (nodeTag(node))
+  	{
+  		case T_Var:
++ 		case T_FakeVar:
+  		case T_Const:
+  		case T_Param:
+  		case T_CoerceToDomainValue:
+***************
+*** 2606,2611 ****
+--- 2627,2642 ----
+  		case T_RangeTblRef:
+  			/* primitive node types with no subnodes */
+  			return (Node *) copyObject(node);
++ 		case T_Prior:
++ 			{
++ 			Prior *expr = (Prior *) node;
++ 			Prior *newnode;
++ 
++ 			FLATCOPY(newnode, expr, Prior);
++ 			MUTATE(newnode->val, expr->val, Node *);
++ 			return (Node *) newnode;
++ 			}
++ 			break;
+  		case T_Aggref:
+  			{
+  				Aggref	   *aggref = (Aggref *) node;
+diff -Prdc --exclude-from=exclude src/backend/optimizer/util/var.c src/backend/optimizer/util/var.c
+*** src/backend/optimizer/util/var.c	Fri Aug  8 21:41:55 2003
+--- src/backend/optimizer/util/var.c	Mon Jul  5 08:19:37 2004
+***************
+*** 225,230 ****
+--- 225,232 ----
+  {
+  	if (node == NULL)
+  		return false;
++ 	if(IsA(node, FakeVar)) /* fake vars are vars too */
++ 		return true;
+  	if (IsA(node, Var))
+  	{
+  		if (((Var *) node)->varlevelsup == 0)
+diff -Prdc --exclude-from=exclude src/backend/parser/analyze.c src/backend/parser/analyze.c
+*** src/backend/parser/analyze.c	Wed Nov  5 22:00:52 2003
+--- src/backend/parser/analyze.c	Mon Jul  5 08:19:37 2004
+***************
+*** 1957,1963 ****
+--- 1957,1969 ----
+  	transformFromClause(pstate, stmt->fromClause);
+  
+  	/* transform targetlist */
++ 	/* set flag for '_level_' column can be selected and aliased in target list
++ 	 * and for PRIOR keyword checks
++ 	 */
++ 	if(stmt->hierClause)
++ 	pstate->p_hierExists = 1; 
+  	qry->targetList = transformTargetList(pstate, stmt->targetList);
++ 	pstate->p_hierExists = 0;
+  
+  	/* handle any SELECT INTO/CREATE TABLE AS spec */
+  	qry->into = stmt->into;
+***************
+*** 1970,1975 ****
+--- 1976,1984 ----
+  	/* transform WHERE */
+  	qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
+  
++ 	/* transform CONNECT BY , START WITH clause */
++ 	qry->hierClause=transformHierClause(pstate,qry,stmt->hierClause);
++ 
+  	/*
+  	 * Initial processing of HAVING clause is just like WHERE clause.
+  	 * Additional work will be done in optimizer/plan/planner.c.
+diff -Prdc --exclude-from=exclude src/backend/parser/gram.y src/backend/parser/gram.y
+*** src/backend/parser/gram.y	Mon Nov 24 16:54:15 2003
+--- src/backend/parser/gram.y	Mon Jul  5 08:19:37 2004
+***************
+*** 318,323 ****
+--- 318,325 ----
+  %type <list>	constraints_set_list
+  %type <boolean> constraints_set_mode
+  
++ %type <node>  startwith_clause
++ %type <list>  connectby_clause hier_clause 
+  
+  /*
+   * If you make any token changes, update the keyword table in
+***************
+*** 336,343 ****
+  	CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P
+  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
+  	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
+! 	COMMITTED CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT COPY CREATE CREATEDB
+! 	CREATEUSER CROSS CURRENT_DATE CURRENT_TIME
+  	CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
+  
+  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
+--- 338,345 ----
+  	CACHE CALLED CASCADE CASE CAST CHAIN CHAR_P
+  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
+  	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
+! 	COMMITTED CONNECT CONSTRAINT CONSTRAINTS CONVERSION_P CONVERT 
+! 	COPY CREATE CREATEDB CREATEUSER CROSS CURRENT_DATE CURRENT_TIME
+  	CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
+  
+  	DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
+***************
+*** 4455,4461 ****
+  simple_select:
+  			SELECT opt_distinct target_list
+  			into_clause from_clause where_clause
+! 			group_clause having_clause
+  				{
+  					SelectStmt *n = makeNode(SelectStmt);
+  					n->distinctClause = $2;
+--- 4457,4463 ----
+  simple_select:
+  			SELECT opt_distinct target_list
+  			into_clause from_clause where_clause
+! 			group_clause hier_clause having_clause
+  				{
+  					SelectStmt *n = makeNode(SelectStmt);
+  					n->distinctClause = $2;
+***************
+*** 4465,4471 ****
+  					n->fromClause = $5;
+  					n->whereClause = $6;
+  					n->groupClause = $7;
+! 					n->havingClause = $8;
+  					$$ = (Node *)n;
+  				}
+  			| select_clause UNION opt_all select_clause
+--- 4467,4474 ----
+  					n->fromClause = $5;
+  					n->whereClause = $6;
+  					n->groupClause = $7;
+!  					n->havingClause = $9;
+!  					n->hierClause = $8;
+  					$$ = (Node *)n;
+  				}
+  			| select_clause UNION opt_all select_clause
+***************
+*** 4487,4492 ****
+--- 4490,4518 ----
+  			| /*EMPTY*/								{ $$ = NULL; }
+  		;
+  
++ hier_clause: connectby_clause startwith_clause 
++  			{ 
++  				$$ = makeList2($1,$2); 
++  			}
++  		| startwith_clause connectby_clause 
++  			{ 
++  				$$ = makeList2($2,$1); 
++  			}
++  		| /* EMPTY */ { $$ = NULL; }
++  		;
++  
++ connectby_clause: CONNECT BY PRIOR c_expr all_Op c_expr
++ 			{ 
++  			  $$ = makeList3($4,makeString($5),$6); 
++  			}
++ 		| CONNECT BY a_expr all_Op PRIOR a_expr
++ 			{ 
++  			  $$ = makeList3($6,makeString($4),$3); 
++  			}
++  		;
++ startwith_clause: START WITH a_expr { $$ = $3; }
++  		;
++  		
+  /*
+   * Redundancy here is needed to avoid shift/reduce conflicts,
+   * since TEMP is not a reserved word.  See also OptTemp.
+***************
+*** 6979,6984 ****
+--- 7005,7026 ----
+  					$$->indirection = NIL;
+  					$$->val = (Node *)$1;
+  				}
++ 			| PRIOR a_expr AS ColLabel
++ 				{
++ 					$$ = makeNode(ResTarget);
++ 					$$->name = $4;
++ 					$$->indirection = NIL;
++ 					$$->val = (Node *)$2;
++ 					$$->prior = 1;
++ 				}
++ 			| PRIOR a_expr
++ 				{
++ 					$$ = makeNode(ResTarget);
++ 					$$->name = NULL;
++ 					$$->indirection = NIL;
++ 					$$->val = (Node *)$2;
++ 					$$->prior = 1;
++ 				}
+  			| '*'
+  				{
+  					ColumnRef *n = makeNode(ColumnRef);
+***************
+*** 7398,7404 ****
+  			| PRECISION
+  			| PREPARE
+  			| PRESERVE
+- 			| PRIOR
+  			| PRIVILEGES
+  			| PROCEDURAL
+  			| PROCEDURE
+--- 7440,7445 ----
+***************
+*** 7428,7434 ****
+  			| SHOW
+  			| SIMPLE
+  			| STABLE
+- 			| START
+  			| STATEMENT
+  			| STATISTICS
+  			| STDIN
+--- 7469,7474 ----
+***************
+*** 7566,7571 ****
+--- 7606,7612 ----
+  			| CHECK
+  			| COLLATE
+  			| COLUMN
++ 			| CONNECT
+  			| CONSTRAINT
+  			| CREATE
+  			| CURRENT_DATE
+***************
+*** 7606,7615 ****
+--- 7647,7658 ----
+  			| ORDER
+  			| PLACING
+  			| PRIMARY
++ 			| PRIOR
+  			| REFERENCES
+  			| SELECT
+  			| SESSION_USER
+  			| SOME
++ 			| START
+  			| TABLE
+  			| THEN
+  			| TO
+diff -Prdc --exclude-from=exclude src/backend/parser/keywords.c src/backend/parser/keywords.c
+*** src/backend/parser/keywords.c	Sat Feb 21 00:35:13 2004
+--- src/backend/parser/keywords.c	Mon Jul  5 08:19:37 2004
+***************
+*** 80,85 ****
+--- 80,86 ----
+  	{"comment", COMMENT},
+  	{"commit", COMMIT},
+  	{"committed", COMMITTED},
++  	{"connect",CONNECT},
+  	{"constraint", CONSTRAINT},
+  	{"constraints", CONSTRAINTS},
+  	{"conversion", CONVERSION_P},
+diff -Prdc --exclude-from=exclude src/backend/parser/parse_clause.c src/backend/parser/parse_clause.c
+*** src/backend/parser/parse_clause.c	Sun Apr 18 18:13:31 2004
+--- src/backend/parser/parse_clause.c	Mon Jul  5 08:19:37 2004
+***************
+*** 38,45 ****
+  #define ORDER_CLAUSE 0
+  #define GROUP_CLAUSE 1
+  #define DISTINCT_ON_CLAUSE 2
+! 
+! static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON"};
+  
+  static void extractRemainingColumns(List *common_colnames,
+  						List *src_colnames, List *src_colvars,
+--- 38,46 ----
+  #define ORDER_CLAUSE 0
+  #define GROUP_CLAUSE 1
+  #define DISTINCT_ON_CLAUSE 2
+! #define CONNECT_CLAUSE 3
+!   
+! static char *clauseText[] = {"ORDER BY", "GROUP BY", "DISTINCT ON","CONNECT BY"};
+  
+  static void extractRemainingColumns(List *common_colnames,
+  						List *src_colnames, List *src_colvars,
+***************
+*** 1285,1290 ****
+--- 1286,1391 ----
+  }
+  
+  /*
++  * transformConnectClause -
++  *	  transform a Connect By clause
++  *
++  */
++ Node *
++ transformHierClause(ParseState *pstate, Query *qry, List *hier)
++ {
++ 	List	   *clist = NIL;	
++ 	TargetEntry *tle;
++ 	HierClause *node;
++ 	List *tlist=qry->targetList,*tl;
++ 	FakeVar *var;
++  	int i,lev_in_tlist=0;
++ 	if(!hier) return NULL;
++  	
++  	node=makeNode(HierClause);
++  	
++  	clist=lfirst(hier);//get conn_list
++ 	
++  	/* add a field '_level_', in result there will be deepness of tuple 
++ 	 * in result tree.
++ 	 * must be added before tranformation parent/child Expr,
++ 	 * because in they transformation process may be added some resjunk
++ 	 * var nodes to tlist, scanRTEForColumn() not supports mix of junk/non-junk
++ 	 * nodes.
++  	 */
++ 	//search for _level_ column in target list, if any use it for 'level' output;
++ 	i = 1;
++ 	foreach(tl,tlist){
++ 	    tle = lfirst(tl);
++ 	    if(IsA(tle->expr,FakeVar)){
++ 		lev_in_tlist = i;
++ 		((FakeVar *)tle->expr)->varattno = i;
++ 		var = (FakeVar *) tle->expr;
++ 		break;
++ 	    }
++ 	    i++;
++ 	}
++ 	if(!lev_in_tlist){
++ 	    var = makeNode(FakeVar);
++     	    var->varattno = 0;
++ 	    var->vartype = INT4OID;
++ 	    var->vartypmod = -1;	
++  	
++  	    tle = transformTargetEntry(pstate, (Node *)var, NULL, "_level_", false);
++  	    lappend(tlist, tle);
++ 	}
++ 	/* transform parent and child expressions */
++ 	pstate->p_curTlist = tlist; /* also see in parser/parse_node.h */
++ 	pstate->p_addTle = true;    /* we may add to tagetlist some junk attributes,
++ 															 * which needed by this expressions but not present in 
++ 															 * user given targetlist. only vars is added */
++ 	node->parentExpr = transformExpr(pstate,lfirst(clist));
++ 	node->childExpr = transformExpr(pstate,lfirst(lnext(lnext(clist))));//lthird
++ 	pstate->p_addTle = false;
++ 	
++ 	/* fetch operatior OID for connecting tuples */
++  	node->connOp = compatible_oper_opid(makeList1(lsecond(clist)),
++ 					exprType(node->parentExpr),
++  					exprType(node->childExpr),
++ 					true);
++ 
++  	if(node->connOp == InvalidOid){
++  	  elog(ERROR,"CONNECT BY: can't find operator '%s' for connecting on chosen fields",(char *)lsecond(clist));
++  	}
++ 	//if connOp eq '==' or '=' assusme it's equality operator and choose to use hash.
++ 	if(!strcmp("=",((Value *)lsecond(clist))->val.str) || !strcmp("==",((Value *)lsecond(clist))->val.str)){
++ 	    node->useHash = 1;
++ 	}	
++ 	
++ 	/* transform StartQual, see note above, on parent/child Expr*/
++ 	pstate->p_addTle = true;
++ 	node->startQual=transformWhereClause(pstate,lsecond(hier),"CONNECT/BY");
++ 	pstate->p_addTle = false;
++ 	pstate->p_curTlist = NULL;// clear
++  	
++  	pstate->p_hierLevelColIdx = tle->resdom->resno;
++ 	
++ 	/* transformExpr return to us our's pointer, so we can to set
++ 	 * attno here, for FakeVar attno is the number in targetlist
++ 	 */
++ 	var->varattno = tle->resdom->resno;
++ 	//store it for further processing
++ 	node->levelColNum = tle->resdom->resno;
++ 	i=0;
++ 
++ 	//process list or prior clauses
++ 	foreach(tl,tlist){
++ 	    TargetEntry *te = lfirst(tl);
++ 	    if(IsA(te->expr,Prior)){
++         node->priorsList=lappend(node->priorsList,makeListi2(i,((Prior *)te->expr)->numcol));
++ 	    }
++ 	    i++;
++ 	}
++ 	node->priorsListNItems = length(node->priorsList);
++  
++  	return (Node *)node;
++ }
++ 
++ /*
+   * transformSortClause -
+   *	  transform an ORDER BY clause
+   */
+diff -Prdc --exclude-from=exclude src/backend/parser/parse_expr.c src/backend/parser/parse_expr.c
+*** src/backend/parser/parse_expr.c	Sun Apr 18 18:13:31 2004
+--- src/backend/parser/parse_expr.c	Mon Jul  5 08:19:37 2004
+***************
+*** 30,35 ****
+--- 30,36 ----
+  #include "parser/parse_oper.h"
+  #include "parser/parse_relation.h"
+  #include "parser/parse_type.h"
++ #include "parser/parse_target.h"
+  #include "utils/builtins.h"
+  #include "utils/lsyscache.h"
+  #include "utils/syscache.h"
+***************
+*** 903,908 ****
+--- 904,910 ----
+  			 * types that are demonstrably necessary to accept.
+  			 *********************************************/
+  		case T_Var:
++     case T_FakeVar:
+  		case T_Const:
+  		case T_Param:
+  		case T_Aggref:
+***************
+*** 937,944 ****
+--- 939,972 ----
+  static Node *
+  transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
+  {
++ 	TargetEntry *tle;
++ 	List *tl;
++ 	bool found;
++ 	
+  	if (indirection == NIL)
++ 	{
++ 		/* If node is Var, and tle addition flag is set then we must add var to targetlist
++ 		 * if it's not presen in tlist
++ 		 * ( also see parser/parse_node.h, parser/parse_clause.c:transformHierClause())
++ 		 */
++ 		if(IsA(basenode, Var) && pstate->p_addTle){
++ 			found = false;
++ 			/* search for var */
++ 			foreach(tl, pstate->p_curTlist){
++ 				tle = lfirst(tl);
++ 				if(equal(basenode,tle->expr)){
++ 					found = true;
++ 					break;
++ 				}
++ 			}
++ 			/* if not fount, make tle and add to tlist */
++ 			if(!found){
++ 				tle = transformTargetEntry(pstate,basenode,basenode,NULL,true);
++ 				lappend(pstate->p_curTlist,tle);
++ 			}
++ 		}
+  		return basenode;
++ 	}
+  	return (Node *) transformArraySubscripts(pstate,
+  											 basenode,
+  											 exprType(basenode),
+***************
+*** 1028,1033 ****
+--- 1056,1084 ----
+  						rv->inhOpt = INH_DEFAULT;
+  						node = (Node *) rv;
+  					}
++           else if (pstate->p_hierLevelColIdx!=0 && !strcmp(name,"_level_"))
++           {
++                 /* If nothing of above and present hier clause and name is '_level_'
++                  * then make fake variable
++                  */
++                 FakeVar *var = makeNode(FakeVar);
++ 
++                 var->varattno = pstate->p_hierLevelColIdx;
++                 var->vartype = INT4OID;
++                 var->vartypmod = -1;
++                 node = (Node *)var;
++           }else if (pstate->p_hierExists!=0 && !strcmp(name,"_level_"))
++ 					{
++ 						/* If nothing of above and present hier clause and name is '_level_'
++ 						 * then make fake variable 
++ 						 */
++ 				 		FakeVar *var = makeNode(FakeVar);
++  		
++ 				 		var->varattno = 0;
++ 				 		var->vartype = INT4OID;
++ 						var->vartypmod = -1;
++ 				 		node = (Node *)var;
++ 				 	}
+  					else
+  						ereport(ERROR,
+  								(errcode(ERRCODE_UNDEFINED_COLUMN),
+***************
+*** 1170,1175 ****
+--- 1221,1229 ----
+  
+  	switch (nodeTag(expr))
+  	{
++ 		case T_FakeVar:
++ 			type = ((FakeVar *) expr)->vartype;
++ 			break;		
+  		case T_Var:
+  			type = ((Var *) expr)->vartype;
+  			break;
+diff -Prdc --exclude-from=exclude src/backend/parser/parse_target.c src/backend/parser/parse_target.c
+*** src/backend/parser/parse_target.c	Thu Sep 25 06:58:01 2003
+--- src/backend/parser/parse_target.c	Mon Jul  5 08:19:37 2004
+***************
+*** 102,107 ****
+--- 102,108 ----
+  {
+  	FastList	p_target;
+  	List	   *o_target;
++  	int prior_count=0;
+  
+  	FastListInit(&p_target);
+  
+***************
+*** 109,114 ****
+--- 110,120 ----
+  	{
+  		ResTarget  *res = (ResTarget *) lfirst(o_target);
+  
++ 		if(res->prior){
++       if(!pstate->p_hierExists)
++         elog(ERROR,"PRIOR keywords can be used only with CONNECT BY clause");
++ 			prior_count++;
++ 		}
+  		if (IsA(res->val, ColumnRef))
+  		{
+  			ColumnRef  *cref = (ColumnRef *) res->val;
+***************
+*** 118,123 ****
+--- 124,131 ----
+  			{
+  				int		numnames = length(fields);
+  
++  				if(res->prior)
++           elog(ERROR,"Reference with PRIOR keyword to * isn't implemented");
+  				if (numnames == 1)
+  				{
+  					/*
+***************
+*** 187,210 ****
+  			else
+  			{
+  				/* Plain ColumnRef node, treat it as an expression */
+! 				FastAppend(&p_target,
+! 						   transformTargetEntry(pstate,
+  												res->val,
+  												NULL,
+  												res->name,
+! 												false));
+  			}
+  		}
+  		else
+  		{
+  			/* Everything else but ColumnRef */
+! 			FastAppend(&p_target,
+! 					   transformTargetEntry(pstate,
+! 											res->val,
+! 											NULL,
+! 											res->name,
+! 											false));
+  		}
+  	}
+  
+  	return FastListValue(&p_target);
+--- 195,273 ----
+  			else
+  			{
+  				/* Plain ColumnRef node, treat it as an expression */
+! 				TargetEntry *te;
+! 				if(res->prior){
+! 					te = transformTargetEntry(pstate,
+! 														res->val,
+! 														NULL,
+! 														res->name,
+! 														false);
+! 					te->prior=1;
+! 				}else{
+!           te = transformTargetEntry(pstate,
+  												res->val,
+  												NULL,
+  												res->name,
+! 												false);
+!         }
+! 				FastAppend(&p_target,te);
+  			}
+  		}
+  		else
+  		{
+  			/* Everything else but ColumnRef */
+! 			TargetEntry *te;
+! 			if(res->prior){
+! 				te = transformTargetEntry(pstate,
+! 													res->val,
+! 													NULL,
+! 													res->name,
+! 													false);
+! 	  		te->prior=1;
+!   		}else{
+!         te = transformTargetEntry(pstate,
+! 									res->val,
+! 									NULL,
+! 									res->name,
+! 									false);
+!       }
+! 			FastAppend(&p_target,te );
+! 		}
+! 	}
+! 	if(prior_count){
+! 		TargetEntry *te,*te1;
+! 		List *tl,*ttl;
+! 		Prior *pr;
+! 		int i=0,c=0,prior_found=0;
+!     bool found;
+! 		foreach(tl,FastListValue(&p_target)){
+! 			te = lfirst(tl);
+! 			if(te->prior){
+!         found = false;
+! 				pr = makeNode(Prior);
+! 				c = 0;
+! 				foreach(ttl,FastListValue(&p_target)){
+! 					te1 = lfirst(ttl);
+!           //skip PRIOR columns, we can't reference to them
+!           if(te1->prior) continue;
+! 					if(equal(te1->expr,te->expr)){
+! 						pr = makeNode(Prior);
+! 						pr->numcol=c;
+! 						pr->val = te->expr;
+! 						te->expr = (Node *)pr;
+!     				prior_found++;
+! 						break;
+! 					}
+! 					c++;
+! 				}
+! 				if(prior_found==(prior_count))
+! 					break;
+! 			}
+! 			i++;
+  		}
++     if(prior_found != prior_count){
++       elog(ERROR,"Expression to which PRIOR references must exist in target list");
++     }
+  	}
+  
+  	return FastListValue(&p_target);
+diff -Prdc --exclude-from=exclude src/backend/utils/adt/ruleutils.c src/backend/utils/adt/ruleutils.c
+*** src/backend/utils/adt/ruleutils.c	Fri May  7 03:20:01 2004
+--- src/backend/utils/adt/ruleutils.c	Mon Jul  5 08:19:37 2004
+***************
+*** 2750,2755 ****
+--- 2750,2758 ----
+  	 */
+  	switch (nodeTag(node))
+  	{
++     case T_FakeVar:
++       appendStringInfo(buf, "FakeVar");
++       break;
+  		case T_Var:
+  			{
+  				Var		   *var = (Var *) node;
+diff -Prdc --exclude-from=exclude src/backend/utils/sort/Makefile src/backend/utils/sort/Makefile
+*** src/backend/utils/sort/Makefile	Thu Aug 31 16:10:59 2000
+--- src/backend/utils/sort/Makefile	Mon Jul  5 08:19:37 2004
+***************
+*** 12,18 ****
+  top_builddir = ../../../..
+  include $(top_builddir)/src/Makefile.global
+  
+! OBJS = logtape.o tuplesort.o tuplestore.o
+  
+  all: SUBSYS.o
+  
+--- 12,18 ----
+  top_builddir = ../../../..
+  include $(top_builddir)/src/Makefile.global
+  
+! OBJS = logtape.o tuplesort.o tuplestore.o tupleconn.o
+  
+  all: SUBSYS.o
+  
+diff -Prdc --exclude-from=exclude src/backend/utils/sort/tupleconn.c src/backend/utils/sort/tupleconn.c
+*** src/backend/utils/sort/tupleconn.c	Thu Jan  1 00:00:00 1970
+--- src/backend/utils/sort/tupleconn.c	Sun Jul 11 08:07:19 2004
+***************
+*** 0 ****
+--- 1,1455 ----
++ /*-------------------------------------------------------------------------
++  *
++  * tupleconn.c
++  *	  Routines for tuple connecting.
++  *
++  * Based on tuplestore.c, tuplesort.c.
++  * (c) by Evgen Potemkin evgent@terminal.ru, 11.2002
++  * 
++  *-------------------------------------------------------------------------
++  */
++ 
++ #include <stdlib.h>
++ #include "postgres.h"
++ 
++ #include "access/heapam.h"
++ #include "storage/buffile.h"
++ #include "utils/tupleconn.h"
++ #include "utils/tuplesort.h"
++ 
++ #include "access/hash.h"
++ #include "access/nbtree.h"
++ #include "catalog/catname.h"
++ #include "catalog/pg_amop.h"
++ #include "catalog/pg_amproc.h"
++ #include "catalog/pg_operator.h"
++ #include "miscadmin.h"
++ #include "utils/datum.h"
++ #include "utils/fmgroids.h"
++ #include "utils/lsyscache.h"
++ #include "utils/syscache.h"
++ 
++ #include "parser/parse_expr.h"
++ #include "nodes/execnodes.h"
++ #include "executor/executor.h"
++ #include "c.h"
++ /*
++  * Possible states of a Tupleconn object.	These denote the states that
++  * persist between calls of Tupleconn routines.
++  */
++ typedef enum
++ {
++ 	TCS_INITIAL,				/* Loading tuples; still within memory
++ 								 * limit */
++ 	TCS_WRITEFILE,				/* Loading tuples; writing to temp file */
++ 	TCS_READMEM,				/* Reading tuples; entirely in memory */
++ 	TCS_READFILE				/* Reading tuples from temp file */
++ } TupConnStatus;
++ 
++ 
++ /* 
++  * connection tree 
++  */
++ typedef struct _ConnectTree ConnectTree;
++ struct _ConnectTree{
++   ConnectTree *next; /* next sibling*/
++   ConnectTree *prev; /* previous sibling */
++   ConnectTree *sub;  /* child */
++   ConnectTree *sup;  /* parent */
++   int  level; /* deep of this node, starts from 1*/
++   int  tuple; /* index of tuple in memtuples/used/out_store */
++ };
++ 
++ /*
++  * Information about tuples stored in buffile
++  */
++ typedef struct OutStore{
++ 	int fileno;	/* BufFile fileno */
++ 	long offs;	/* BufFile offs */
++ 	int len;		/* tuple length*/
++ 	HeapTuple tup;	/* in-memory tuple copy, NULL if none */
++ }OutStore;
++ 
++ /*
++  * Datum cache
++  */
++ typedef struct DtCache{
++ 	Datum pntdt_val;	/* cached pntExpr value of tuple*/
++ 	Datum chlddt_val;	/* cached chdlExpr value of tuple*/
++ 	bool pntdt_isnull;	/* cached pntExpr is_null value of tuple*/
++ 	bool chlddt_isnull;	/* cached chdlExpr is_null value of tuple*/
++ 	bool pnt_c;	/* parent value is cached flag*/
++ 	bool chld_c;	/* child value is cached flag*/
++ } DtCache; 
++ 
++ //cached values of 1 tuple 
++ typedef struct _HashTableListElem HashTableListElem;
++ struct _HashTableListElem{
++ 	uint32 pnt_hash;
++ 	bool pnt_isnull;
++ 	Datum pnt_val;
++ 	Datum chld_val;
++ 	uint tup_idx;
++ 	HashTableListElem *next;
++ };
++ /*
++  * Private state of a Tupleconn operation.
++  */
++ struct Tupleconnstate
++ {
++ 	TupConnStatus status;		/* enumerated value as shown above */
++ 	long		availMem;		/* remaining memory available, in bytes */
++ 	BufFile    *myfile;			/* underlying file, or NULL if none */
++ 
++ 	/*
++ 	 * Tree and supporting stuff, explanation at _performconn function 
++ 	 */
++ 	MemoryContext tree_ctx;	
++   ConnectTree *head; /* head of the result list */
++ 	ConnectTree **pnt;  /* parent nodes on curent level*/
++ 	int pntlast;	/* actual amount of parents on current level */
++ 	int pntsize;	/* size of pnt array */
++ 	ConnectTree **chld; /* array of child nodes */
++ 	int chldlast; /* actual amount of childs on current level */
++ 	int chldsize;	/* size of chld array */
++ 	ConnectTree *last; /* last added/fetched node */	
++   bool skip_node; /* used in gettuple, mean don't fall to ->sub because 
++ 	                * we're going from that*/
++ 	char *used;	/* array of flags - was tuple (connected already|is null) or not */
++   TupleDesc tupDesc;
++ 	AttrNumber level; /* tuple's '_level_' attribute number */
++ 	FmgrInfo connFn; /* comparation function */
++ 	
++ 	OutStore *out_store; /* array of info about tuples in buffile for faster random access */
++ 	DtCache *cache; /* cache for datums returned by ExecEvalExpr */
++ 
++ 	HashTableListElem *hash_list;
++ 	HashTableListElem **hash_tbl;
++ 	int hash_tbl_size;
++ 	int nbuckets;
++   bool use_hash;  
++ 
++ 	void	   *(*copytup) (Tupleconnstate *state, void *tup);
++ 	void		(*writetup) (Tupleconnstate *state, void *tup, bool free, int idx);
++ 	void	   *(*readtup) (Tupleconnstate *state, int idx);
++ 
++ 	void	  **memtuples;		/* array of pointers to palloc'd tuples */
++ 	int			memtupsize;		/* allocated length of memtuples array */
++ 
++ 	int			tupcount;	/* number of tuples currently present */
++ 
++ 	bool		eof_reached;	/* reached EOF (needed for cursors) */
++ 
++ 	/* markpos_xxx holds marked position for mark and restore */
++ 	bool		markpos_eof;	/* saved "eof_reached" */
++ 	ConnectTree *markpos_last; /* saved "last" pointer */
++ 	bool		markpos_skip;	/* saved "skip" flag */
++   List *priorsList; 
++   void *prev_tuple;
++ };
++ 
++ 
++ #define COPYTUP(state,tup)	((*(state)->copytup) (state, tup))
++ #define WRITETUP(state,tup,free,idx) ((*(state)->writetup) (state, tup, free, idx))
++ #define READTUP(state,idx)	((*(state)->readtup) (state, idx))
++ #define LACKMEM(state)		((state)->availMem < 0)
++ #define USEMEM(state,amt)	((state)->availMem -= (amt))
++ #define FREEMEM(state,amt)	((state)->availMem += (amt))
++ #define INIT_GUESS 1024
++ 
++ 
++ static Tupleconnstate *tupleconn_begin_common(int maxKBytes);
++ static void dumptuples(Tupleconnstate *state);
++ 
++ static void *copytup_heap(Tupleconnstate *state, void *tup);
++ static void writetup_heap(Tupleconnstate *state, void *tup, bool free, int idx);
++ static void *readtup_heap(Tupleconnstate *state, int idx);
++ void SelectConnFunction(Oid sortOperator,RegProcedure *sortFunction,
++ 				   SortFunctionKind *kind);
++ void *setlevel(Tupleconnstate * state,int level, void *tuple, bool free_it, void *prev_tuple);
++ 
++ ConnectTree *add_next(Tupleconnstate * state,ConnectTree *tr,int tup,int level);
++ ConnectTree *add_sub(Tupleconnstate * state,ConnectTree *tr,int tup,int level);
++ 
++ static uint32 hashFunc(Datum key, int typLen, bool byVal);
++ 
++ void tupleconn_performconn_hash(Tupleconnstate *state, Node *parentExpr, Node *childExpr, 
++     ExprContext *econtext);
++ /*
++  * Init all
++  */
++ static Tupleconnstate *
++ tupleconn_begin_common(int maxKBytes)
++ {
++ 	Tupleconnstate *state;
++ 	
++ 	state = (Tupleconnstate *) palloc(sizeof(Tupleconnstate));
++ 
++ 	MemSet((char *) state, 0, sizeof(Tupleconnstate));
++ 
++ 	state->status = TCS_INITIAL;
++ 	state->availMem = maxKBytes * 1024L;
++ 	state->myfile = NULL;
++ 	state->out_store = NULL;
++ 	
++ 	state->tree_ctx = AllocSetContextCreate(CurrentMemoryContext,"Temporary connby storage",8*1024,8*1024,8*1024);
++ 
++ 	state->tupcount = 0;
++ 	if (maxKBytes > 0)
++ 		state->memtupsize = INIT_GUESS;		/* initial guess */
++ 	else
++ 		state->memtupsize = 1;	/* won't really need any space */
++ 	state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
++ 
++   state->pntlast=0;
++   state->chldlast=0;
++ 	state->pntsize=INIT_GUESS;		/* initial guess */
++ 	state->chldsize=INIT_GUESS;		/* initial guess */
++ 	state->pnt=(ConnectTree **)palloc(state->pntsize * sizeof(void *));
++ 	state->chld=(ConnectTree **)palloc(state->pntsize * sizeof(void *));
++ 
++   state->used = (char *) palloc(state->memtupsize * sizeof(char));
++   state->level=0;
++ 	return state;
++ }
++ 
++ Tupleconnstate *
++ tupleconn_begin_heap(int maxKBytes, TupleDesc tupDesc, Oid connOp, List *priorsList, 
++   int levelColNum, double rows,bool useHash)
++ {
++   RegProcedure connFunc;
++ 	SortFunctionKind connFnKind;
++ 	double td;
++   MemoryContext morig;
++ 	
++ 	Tupleconnstate *state = tupleconn_begin_common(maxKBytes);
++ 
++ 	state->copytup = copytup_heap;
++ 	state->writetup = writetup_heap;
++ 	state->readtup = readtup_heap;
++ 
++   state->tupDesc=tupDesc;
++ 	
++ 	SelectConnFunction(connOp, &connFunc,
++ 						   &connFnKind);
++ 	if(connFnKind != SORTFUNC_CMP){
++ 	  elog(ERROR,"tupleconn: Can't find suitable function for comparison");
++ 	}
++ 	fmgr_info(connFunc,&state->connFn);
++   state->priorsList = priorsList;
++   state->level = levelColNum - 1;
++   if(useHash){
++     state->use_hash = true;
++   	td = rows/5;
++   	if(td > INT_MAX)
++ 	  	td = INT_MAX;
++   	state->nbuckets = (int)td;//assume there is 8 tuples in bucket
++ 	  if(state->nbuckets==0)
++ 		  state->nbuckets=1;
++     morig = MemoryContextSwitchTo(state->tree_ctx);
++   	state->hash_list = (HashTableListElem *)palloc(sizeof(HashTableListElem)*INIT_GUESS);	//initial guess
++ 	  state->hash_tbl = (HashTableListElem **)palloc(sizeof(void *)*state->nbuckets);	//initial guess
++    	MemoryContextSwitchTo(morig);
++     MemSet(state->hash_list, 0, sizeof(HashTableListElem)*INIT_GUESS);	//initial guess
++     MemSet(state->hash_tbl, 0, sizeof(void *)*state->nbuckets);	//initial guess
++   
++     state->used = (char *) palloc(state->memtupsize * sizeof(char));
++     MemSet(state->used, 0, sizeof(char)*state->memtupsize);	//initial guess
++   }else
++     state->use_hash = false;
++ 
++ 	return state;
++ }
++ /*
++  * tupleconn_end
++  *
++  *	Release resources and clean up.
++  */
++ void
++ tupleconn_end(Tupleconnstate *state)
++ {
++ 	int	i;
++ 	/* common frees */
++ 	pfree(state->pnt);
++ 	pfree(state->chld);
++ 	pfree(state->used);
++ 	/* free tree, if any */
++ 	state->last = state->head;
++ 	// release tree and cache
++ 	MemoryContextDelete(state->tree_ctx);
++ 	/* free tuples, from out_store or memtuples*/
++ 	if (state->myfile){
++ 		BufFileClose(state->myfile);
++ 		for(i=0;i<state->tupcount;i++){
++ 			if(state->out_store[i].tup!=NULL){
++ 				pfree(state->out_store[i].tup);
++ 				FREEMEM(state,state->out_store[i].len);
++ 				state->out_store[i].tup=NULL;
++ 			}
++ 		}
++ 	}else{
++ 		for (i = 0; i < state->tupcount; i++)
++ 			pfree(state->memtuples[i]);
++ 		pfree(state->memtuples);
++ 	}
++ }
++ 
++ /*
++  * Accept one tuple while collecting input data.
++  *
++  * Note that the input tuple is always copied; the caller need not save it.
++  */
++ void
++ tupleconn_puttuple(Tupleconnstate *state, void *tuple,int head)
++ {
++ 	/*
++ 	 * Copy the tuple.	(Must do this even in WRITEFILE case.)
++ 	 */
++ 	tuple = COPYTUP(state, tuple);
++ 
++ 	/* common thing */
++   if(head){//it's a head tuple,add it to head list
++     //add autogrow for pnt
++ 	  state->last = add_next(state,state->last,state->tupcount,1);
++     if(!state->head) state->head = state->last;
++     state->pnt[state->pntlast++] = state->last;
++ 		state->used[state->tupcount] = 1;
++   }else{
++ 	  state->used[state->tupcount] = 0;
++ 	}
++ 	
++ 	switch (state->status)
++ 	{
++ 		case TCS_INITIAL:
++ 
++ 			/*
++ 			 * Stash the tuple in the in-memory array.
++ 			 */
++ 			if (state->tupcount >= state->memtupsize-1)
++ 			{
++ 				/* Grow the arrays as needed. */
++ 				state->memtupsize *= 2;
++ 				state->memtuples = (void **)
++ 					repalloc(state->memtuples,
++ 							 state->memtupsize * sizeof(void *));
++ 				state->used = (char *)
++ 					repalloc(state->used,
++ 							 state->memtupsize * sizeof(char));
++ 				MemSet((char *) (state->used+sizeof(char)*(state->memtupsize>>1)), 0, sizeof(char)*(state->memtupsize>>1));
++ 			}
++ 			state->memtuples[state->tupcount++] = tuple;
++ 			/*
++ 			 * Done if we still fit in available memory.
++ 			 */
++ 			if (!LACKMEM(state))
++ 				break;
++ 
++ 			/*
++ 			 * Nope; time to switch to tape-based operation.
++ 			 */
++ 			state->myfile = BufFileCreateTemp(false);			
++ 			state->status = TCS_WRITEFILE;
++ 			state->out_store = palloc(state->memtupsize * sizeof(OutStore));
++ 			dumptuples(state);
++ 			break;
++ 		case TCS_WRITEFILE:
++ 			if (state->tupcount >= state->memtupsize-1)
++ 			{
++ 				/* Grow the arrays as needed. */
++ 				state->memtupsize *= 2;
++ 				state->out_store = (OutStore *)
++ 					repalloc(state->out_store,
++ 							 state->memtupsize * sizeof(OutStore));
++ 				state->used = (char *)
++ 					repalloc(state->used,
++ 							 state->memtupsize * sizeof(char));
++ 				MemSet((char *) (state->used+sizeof(char)*(state->memtupsize>>1)), 0, sizeof(char)*(state->memtupsize>>1));
++ 			}
++ 			WRITETUP(state, tuple,!head,state->tupcount++);
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_puttuple: invalid state (internal error)");
++ 			break;
++ 	}
++ }
++ 
++ /*
++  * All tuples have been provided; finish writing.
++  */
++ void
++ tupleconn_donestoring(Tupleconnstate *state)
++ {
++ 	switch (state->status)
++ 	{
++ 		case TCS_INITIAL:
++ 			/*
++ 			 * We were able to accumulate all the tuples within the
++ 			 * allowed amount of memory.  Just set up to connect and scan them.
++ 			 */
++ 			state->status = TCS_READMEM;
++ 			break;
++ 		case TCS_WRITEFILE:
++ 			/*
++ 			 * Set up for connecting/reading from tape.
++ 			 */
++ 			state->status = TCS_READFILE;
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_donestoring: invalid state ");
++ 			break;
++ 	}
++ 	state->eof_reached = false;
++ 	state->markpos_eof = false;
++ 	state->last=state->head;
++ }
++ 
++ /*
++  * tupleconn_performconn: perform connection on tuples
++  * Algorithm: in puttuple has been made list of top parent nodes,
++  * in each iteration we try to find all non-connected tuples which
++  * 'child' attribute is equal to 'parent' attribute in one of parent 
++  * nodes, if so - tuple becomes a child of corresponding parent node.
++  * at end of iteration collected childs becomes the parents for next 
++  * iteration. 
++  * If no childs were find algorithm stops.
++  * Scan for childs in one iteration going on full array of stored 
++  * tuples - this preserves order of tuples from subplan. for example 
++  * if subplan was alphabetically sorted, childs on one level of each 
++  * parent will be also alphabetically sorted.
++  * In case of file storage at end of algorithm all tuples resides only 
++  * on tape.
++  * 
++  * Clause was:
++  * CONNECT BY PRIOR expr Op expr
++  * here:
++  * PRIOR expr is - parentExpr, checked against parent tuple
++  * Op - is connOp operator, performs comparation
++  * expr (the rest) - is childExpr, checked against child tuple
++  */
++ void
++ tupleconn_performconn(Tupleconnstate *state, 
++ 					Node *parentExpr, Node *childExpr, ExprContext *econtext){
++   int ok=0,i,j,ti,level=1;
++ 	ConnectTree **t;
++ 	int32 is_parent;
++ 	Datum dt1,dt2;
++ 	bool p_isnull,isnull,conn;
++ 	HeapTuple pnt,chld;
++ 	TupleDesc tupDesc;
++ 	TupleTableSlot *slot;
++ 	MemoryContext morig;
++   int16	pnt_typlen;
++ 	bool	pnt_typbyval;
++   int16	chld_typlen;
++ 	bool	chld_typbyval;
++ 	Size	datalen;
++ 	char	*newVal;
++   ExprState *parentExprState,*childExprState;
++ 
++ 	if(!state->head) return; /* trivial case, don't connect anything */
++   if(state->use_hash)
++     return tupleconn_performconn_hash(state, parentExpr, childExpr, econtext);
++ 
++ 	/* check status */
++ 	switch(state->status){
++ 		case TCS_READMEM:
++ 		case TCS_READFILE:
++ 			break;
++ 		default:
++ 			elog(ERROR,"tupleconn: invalid state in performconn (internal error)");
++ 	}
++ 
++   parentExprState=makeNode(ExprState);
++   parentExprState->expr=(Expr *)parentExpr;
++   childExprState=makeNode(ExprState);
++   childExprState->expr=(Expr *)childExpr;
++ 	tupDesc=state->tupDesc;
++ 	
++ 	/* get child and parent exprs typlens for cache */	
++ 	get_typlenbyval(exprType(parentExpr), &pnt_typlen, &pnt_typbyval);
++ 	get_typlenbyval(exprType(childExpr), &chld_typlen, &chld_typbyval);
++ 	
++ 	/* alloc cache in temp space */
++ 	morig = MemoryContextSwitchTo(state->tree_ctx);
++ 	state->cache = (DtCache *)palloc(sizeof(DtCache)*state->tupcount);	
++ 	MemoryContextSwitchTo(morig);
++ 	MemSet(state->cache, 0, sizeof(DtCache)*state->tupcount);
++ 
++ 	/* make for ExecEvalExpr temporary slot */
++ 	slot=makeNode(TupleTableSlot);
++ 	slot->ttc_tupleDescriptor = tupDesc;
++ 	slot->ttc_buffer = InvalidBuffer;
++ 	slot->ttc_shouldFreeDesc = false;
++ 	slot->ttc_shouldFree = true;
++ 	slot->ttc_descIsNew = false;
++ 	
++ 	/* set slot for ExecExvalExpr */
++ 	econtext->ecxt_scantuple = slot;
++ 	
++   while(!ok){
++ 	  ok=1;
++  		for(i=0;i<state->tupcount;i++){//scan through array of tuples
++ 			/* skip already connected and null tuples */
++ 			CHECK_FOR_INTERRUPTS();
++      	if(!state->used[i]){
++ 				ResetExprContext(econtext);
++ 				/* get tuple for connecting */
++ 				/* if cached - use cache, in not - retrieve tuple, and build cache */
++ 				if(state->cache[i].chld_c){
++ 					dt1 = state->cache[i].chlddt_val;
++ 					isnull = state->cache[i].chlddt_isnull;
++ 				}else{
++ 					if(state->status == TCS_READMEM) {
++ 						chld=(HeapTuple)state->memtuples[i];
++ 						slot->val = chld;
++ 						dt1 = ExecEvalExpr(childExprState,econtext,&isnull,NULL);
++ 					}	else { //READFILE
++ 						chld=READTUP(state,i);
++ 						Assert(chld!=NULL);
++ 						slot->val = chld;
++ 						/* get value of child expr,
++ 						 * in case of variable node is equal to 
++ 					   * dt1=heap_getattr(chld, attno1, tupDesc, &isnull); 
++ 						 */
++ 						dt1 = ExecEvalExpr(childExprState,econtext,&isnull,NULL);						
++ 					}
++ 					/* no need in storing isnull, because when we first time 
++ 					 * evaluating expr and it's null then it marked as used 
++ 					 * and don't checked anymore, thus cache not involved
++ 					 */					 
++ 					state->cache[i].chld_c = true;
++ 					/* store it if it's not null */
++ 					if(!isnull){
++ 						if(chld_typbyval){
++ 							state->cache[i].chlddt_val = dt1;
++ 						}else {
++ 							/* copy datum data to temp space*/
++ 							datalen = datumGetSize(dt1, false, chld_typlen);
++ 							morig = MemoryContextSwitchTo(state->tree_ctx);
++ 							newVal = (char *) palloc(datalen);
++ 							MemoryContextSwitchTo(morig);
++ 							memcpy(newVal, DatumGetPointer(dt1), datalen);
++ 							state->cache[i].chlddt_val = PointerGetDatum(newVal);
++ 							state->cache[i].chlddt_isnull = false;
++ 							dt1 = PointerGetDatum(newVal);
++ 						}
++ 					}
++ 					/* free tuple, since we don't need it further, till _gettuple */
++ 					if(state->status != TCS_READMEM) {
++ 						pfree(chld);
++ 						FREEMEM(state,((HeapTuple)chld)->t_len+HEAPTUPLESIZE);
++ 						state->out_store[i].tup=NULL;						
++ 					}
++ 				}
++ 				conn=false;
++ 				if(!isnull){
++ 					/* scan through nodes array of previous level,
++ 					 * until connect it or array is exhausted
++ 					 */
++ 				  for(j=0;j<state->pntlast && !conn;j++){
++ 						/* get parent tuple */
++ 						/* if cached - use cache, in not - retrieve tuple, and build cache */
++ 						if(state->cache[state->pnt[j]->tuple].pnt_c){
++ 							dt2 = state->cache[state->pnt[j]->tuple].pntdt_val;
++ 							p_isnull = state->cache[state->pnt[j]->tuple].pntdt_isnull;
++ 						}else{
++ 							if(state->status == TCS_READMEM) {
++ 							  pnt=(HeapTuple)state->memtuples[state->pnt[j]->tuple];
++ 								slot->val = pnt;
++ 								dt2 = ExecEvalExpr(parentExprState,econtext,&p_isnull,NULL);
++ 							} else {
++ 								// if tuple parent value is not cached and tuple isn't in mem, 
++ 								// then read it and build cache
++ 								pnt=state->out_store[state->pnt[j]->tuple].tup;
++ 						
++ 								if(pnt==NULL){
++ 									pnt=READTUP(state,state->pnt[j]->tuple);
++ 								}
++ 								Assert(pnt!=NULL); /*elog(ERROR,"tupleconn: parent tuple is null (internal error)");*/
++ 
++ 								slot->val = pnt;
++ 								/* get value of parent expr */
++ 								dt2 = ExecEvalExpr(parentExprState,econtext,&p_isnull,NULL);
++ 							}
++ 							state->cache[state->pnt[j]->tuple].pntdt_isnull = p_isnull;
++ 							state->cache[state->pnt[j]->tuple].pnt_c = true;
++ 							/* cache it, if it's not null*/
++ 							if(!p_isnull){
++ 								if(pnt_typbyval){
++ 									state->cache[state->pnt[j]->tuple].pntdt_val = dt2;
++ 								}else{
++ 									/* copy datum data to temp space */
++ 									datalen = datumGetSize(dt2, false, pnt_typlen);
++ 									morig = MemoryContextSwitchTo(state->tree_ctx);
++ 									newVal = (char *) palloc(datalen);
++ 									MemoryContextSwitchTo(morig);
++ 									memcpy(newVal, DatumGetPointer(dt2), datalen);
++ 									state->cache[state->pnt[j]->tuple].pntdt_val = PointerGetDatum(newVal);
++ 									state->cache[state->pnt[j]->tuple].pntdt_isnull = false;
++ 									dt2 = PointerGetDatum(newVal);
++ 								}
++ 							}
++ 							/* free tuple, same as child */
++ 							if(state->status != TCS_READMEM) {
++ 								pfree(pnt);
++ 								FREEMEM(state,((HeapTuple)pnt)->t_len+HEAPTUPLESIZE);
++ 								state->out_store[state->pnt[j]->tuple].tup = NULL;
++ 							}
++ 						}
++ 						if(!p_isnull){
++ 							/* apply connOp operator on parent and child expr results,
++ 							 * if 0 (in case of CMP means they're equal) connect them
++ 							 */
++ 							is_parent=DatumGetInt32(FunctionCall2(&state->connFn,dt1,dt2));
++ 							if(is_parent==0){
++ 		        	  ok=0;
++ 								/* stop scan of parents */
++ 								conn=true;
++ 								/* connect tuples (make node of the connect tree)*/
++  			        	state->chld[state->chldlast++]=add_sub(state,state->pnt[j],i,level+1);
++ 								state->used[i]=1;
++ 								if(state->chldlast>=state->chldsize-1){
++ 									/* grow array of connected tuples as necessary */
++ 									state->chldsize *= 2;
++ 									state->chld = (ConnectTree **)
++ 											repalloc(state->chld,
++ 									 		state->chldsize * sizeof(void *));
++ 								}
++   	       		}
++ 						}
++  		    	}
++ 				}else{
++ 					/* mark it as used since it has null child value and can't be 
++ 					 * connected to any node
++ 					 * may be better to add nullcheck on this field at parse stage to whereClause, 
++ 					 */
++ 					state->used[i]=1;
++ 				}
++ 			}
++  	  }
++ 		/* swap pnt & chld arrays */
++ 		t=state->pnt;
++ 		ti=state->pntsize;
++ 
++    	state->pnt=state->chld;
++ 		state->pntsize=state->chldsize;
++ 		state->pntlast=state->chldlast;
++ 
++ 		state->chld=t;
++ 		state->chldsize=ti;
++ 		state->chldlast=0;
++ 
++  	  level++;
++  	}
++ 	/* free anything temporal */
++ 	ResetExprContext(econtext);
++ 	pfree(slot);
++ 	econtext->ecxt_scantuple = NULL;
++ 	pfree(state->cache);
++ 	state->cache=NULL;
++ }
++ 
++ void
++ tupleconn_performconn_hash(Tupleconnstate *state,
++ 					Node *parentExpr, Node *childExpr, ExprContext *econtext){
++   int ok=0,i,j,ti,level=1,k;
++ 	ConnectTree **t;
++ 	int32 is_parent;
++ 	Datum dt1,dt2;
++ 	bool isnull,conn;
++ 	HeapTuple tuple;
++ 	TupleDesc tupDesc;
++ 	TupleTableSlot *slot;
++ 	MemoryContext morig;
++   int16	pnt_typlen;
++ 	bool	pnt_typbyval;
++   int16	chld_typlen;
++ 	bool	chld_typbyval;
++ 	Size	datalen;
++ 	char	*newVal;
++ 	uint32 h1,h2;
++ 	uint bucket;  
++ 	HashTableListElem *htle;
++   ExprState *parentExprState,*childExprState;
++ 
++ 	if(!state->head) return; /* trivial case, don't connect anything */
++ 	/* check status */
++ 	switch(state->status){
++ 		case TCS_READMEM:
++ 		case TCS_READFILE:
++ 			break;
++ 		default:
++ 			elog(ERROR,"tupleconn: invalid state in performconn (internal error)");
++ 	}
++ 
++   parentExprState=makeNode(ExprState);
++   parentExprState->expr=(Expr *)parentExpr;
++   childExprState=makeNode(ExprState);
++   childExprState->expr=(Expr *)childExpr;
++ 	tupDesc=state->tupDesc;
++ 	/* get child and parent exprs typlens for cache */	
++ 	get_typlenbyval(exprType(parentExpr), &pnt_typlen, &pnt_typbyval);
++ 	get_typlenbyval(exprType(childExpr), &chld_typlen, &chld_typbyval);
++ 	
++ 	/* alloc cache in temp space */
++ 	morig = MemoryContextSwitchTo(state->tree_ctx);
++ 	state->hash_list = (HashTableListElem *)palloc(sizeof(HashTableListElem)*state->tupcount);	
++ 	MemoryContextSwitchTo(morig);
++ 	MemSet(state->hash_list, 0, sizeof(HashTableListElem)*state->tupcount);
++ 
++ 	/* make for ExecEvalExpr temporary slot */
++ 	slot=makeNode(TupleTableSlot);
++ 	slot->ttc_tupleDescriptor = tupDesc;
++ 	slot->ttc_buffer = InvalidBuffer;
++ 	slot->ttc_shouldFreeDesc = false;
++ 	slot->ttc_shouldFree = true;
++ 	slot->ttc_descIsNew = false;
++ 	
++ 	/* set slot for ExecExvalExpr */
++ 	econtext->ecxt_scantuple = slot;
++ 
++   /*
++    * build hash table
++    * for all tuples, calculate hash for childExpr. if tuple is head then 
++    * calc parentExpr hash instead. if either Expr is null, throw tuple out.
++    */
++   for(i=0;i<state->tupcount;i++){
++     //get tuple
++     if(state->status == TCS_READMEM) {
++ 			tuple=(HeapTuple)state->memtuples[i];
++ 		}	else { //READFILE
++ 			tuple=READTUP(state,i);
++ 			Assert(tuple!=NULL);
++   	}
++     
++ 		slot->val = tuple;
++     state->hash_list[i].tup_idx = i;
++     ResetExprContext(econtext);
++ 		if(state->used[i]){ //for now it means 'head'
++   	  dt2 = ExecEvalExpr(parentExprState,econtext,&isnull,NULL);
++       if(!isnull) {
++         h2 = hashFunc(dt2, pnt_typlen, pnt_typbyval);
++   			//fill pnt_hash value for perfconn
++ 				//no need to add this tuple to hash_table, we never will use it as child tuple
++ 	      state->hash_list[i].pnt_hash = h2;
++ 
++ 				if(pnt_typbyval || state->status == TCS_READMEM){
++ 					state->hash_list[i].pnt_val = dt2;
++ 				}else {
++ 					/* copy datum data to temp space*/
++ 					datalen = datumGetSize(dt2, false, pnt_typlen);
++ 					morig = MemoryContextSwitchTo(state->tree_ctx);
++ 					newVal = (char *) palloc(datalen);
++ 					MemoryContextSwitchTo(morig);
++ 					memcpy(newVal, DatumGetPointer(dt2), datalen);
++ 					state->hash_list[i].pnt_val = PointerGetDatum(newVal);
++ 					state->hash_list[i].pnt_isnull = false;
++ 				}
++ 				/* free tuple, since we don't need it further, till _gettuple */
++       }else{
++         state->hash_list[i].pnt_isnull = true;
++       }
++ 		}else{
++ 			uint bucket;
++ 			HashTableListElem *htle;
++ 			MemoryContext morig;
++ 
++   	  dt1 = ExecEvalExpr(childExprState,econtext,&isnull,NULL);
++ 			if(!isnull) {// sanity check
++         
++         h1 =	hashFunc(dt1, chld_typlen, chld_typbyval);
++   			bucket = h1 % state->nbuckets;
++ 
++ 				if(chld_typbyval || state->status == TCS_READMEM){
++ 					state->hash_list[i].chld_val = dt1;
++ 				}else {
++ 						/* copy datum data to temp space*/
++ 					datalen = datumGetSize(dt1, false, chld_typlen);
++ 					morig = MemoryContextSwitchTo(state->tree_ctx);
++ 					newVal = (char *) palloc(datalen);
++ 					MemoryContextSwitchTo(morig);
++ 					memcpy(newVal, DatumGetPointer(dt1), datalen);
++ 					state->hash_list[i].chld_val = PointerGetDatum(newVal);
++ 				}
++         // add tuple to bucket
++ 				if(state->hash_tbl[bucket]){
++ 					for(k=0,htle = state->hash_tbl[bucket];htle->next;htle=htle->next);//k++
++ 					htle->next=&state->hash_list[i];
++ 				}else{
++ 					state->hash_tbl[bucket] = &state->hash_list[i];
++ 				} 
++       }else{
++         //it's null, throw it out
++         state->used[i] = 1;
++       }
++ 	
++   	}
++ 		/* free tuple, since we don't need it further, till _gettuple */
++   	if(state->status != TCS_READMEM) {
++ 			pfree(tuple);
++ 			FREEMEM(state,((HeapTuple)tuple)->t_len+HEAPTUPLESIZE);
++ 			state->out_store[i].tup=NULL;						
++ 		}
++   }
++   /*
++    * perform connection of tuples
++    * for each head tuple calculate bucket from parent hash, then check connect conditions on 
++    * every tuple in bucket list, if accepted add to tree.
++    */
++   /*
++    * process list of parents, if no childs are found (ok==1), then tree building is complete.
++    */
++   while(!ok){
++ 	  ok=1;
++ 		CHECK_FOR_INTERRUPTS();
++ 		conn=false;
++           
++ 		for(j=0;j<state->pntlast;j++){
++ 			int pnt_tup_idx;
++ 			/* get parent tuple */
++ 			/* if cached - use cache, in not - retrieve tuple, and build cache */
++ 			pnt_tup_idx = state->pnt[j]->tuple;
++ 			if(state->hash_list[pnt_tup_idx].pnt_isnull) continue;
++ 			h2 = state->hash_list[pnt_tup_idx].pnt_hash;
++ 			dt2 = state->hash_list[pnt_tup_idx].pnt_val;
++ 
++ 			/* get bucket */
++ 			bucket = h2 % state->nbuckets;
++ 
++       i=0;k=0;
++       /* search bucket for child tuples */
++ 			for(htle=state->hash_tbl[bucket];htle;htle = htle->next){
++ 				if(!htle)
++ 					break;
++         //skip it if null or the self
++ 				if(state->used[htle->tup_idx] || pnt_tup_idx == htle->tup_idx )
++ 					continue;
++         //check connect condition
++ 				is_parent=DatumGetInt32(FunctionCall2(&state->connFn,htle->chld_val,dt2));
++ 				if(is_parent !=0)//isn't accepted
++ 					continue;
++          // found one or more childs
++ 		    ok=0;
++         // calc parentExpr hash, it will be used on next round
++ 				if(!htle->pnt_hash){
++ 					Datum dt;
++ 					bool isnull;
++ 					ResetExprContext(econtext);
++           
++           if(state->status == TCS_READMEM) {
++       			tuple=(HeapTuple)state->memtuples[htle->tup_idx];
++       		}	else { //READFILE
++       			tuple=READTUP(state,htle->tup_idx);
++       			Assert(tuple!=NULL);
++         	}
++ 					slot->val = tuple;
++ 
++ 				  dt = ExecEvalExpr(parentExprState,econtext,&isnull,NULL);
++ 			    if(!isnull) {
++             int tttt;
++ 						htle->pnt_hash = hashFunc(dt, pnt_typlen, pnt_typbyval);
++             tttt = htle->pnt_hash % state->nbuckets;
++       			if(pnt_typbyval){
++       				htle->pnt_val = dt;
++       			}else {
++       				/* copy datum data to temp space*/
++       				datalen = datumGetSize(dt, false, pnt_typlen);
++       				morig = MemoryContextSwitchTo(state->tree_ctx);
++       				newVal = (char *) palloc(datalen);
++       				MemoryContextSwitchTo(morig);
++       				memcpy(newVal, DatumGetPointer(dt), datalen);
++       				htle->pnt_val = PointerGetDatum(newVal);
++       				htle->pnt_isnull = false;
++       			}
++           	if(state->status != TCS_READMEM) {
++         			pfree(tuple);
++         			FREEMEM(state,((HeapTuple)tuple)->t_len+HEAPTUPLESIZE);
++         			state->out_store[i].tup=NULL;						
++         		}
++ 					}else
++ 						htle->pnt_isnull = true;
++ 				}
++ 				/* connect tuples (make node of the connect tree)*/
++  		  	state->chld[state->chldlast++]=add_sub(state,state->pnt[j],htle->tup_idx,level+1);
++         state->used[htle->tup_idx] = 1;
++ 				if(state->chldlast>=state->chldsize-1){
++ 					/* grow array of connected tuples as necessary */
++ 					state->chldsize *= 2;
++ 					state->chld = (ConnectTree **)
++ 							repalloc(state->chld,
++ 					 		state->chldsize * sizeof(void *));
++ 				}
++         i++;
++   	 	}
++  		}
++ 		/* swap pnt & chld arrays */
++ 		t=state->pnt;
++ 		ti=state->pntsize;
++ 
++    	state->pnt=state->chld;
++ 		state->pntsize=state->chldsize;
++ 		state->pntlast=state->chldlast;
++ 
++ 		state->chld=t;
++ 		state->chldsize=ti;
++ 		state->chldlast=0;
++  	  level++;
++  	}
++ 	/* free anything temporal */
++ 	ResetExprContext(econtext);
++ 	pfree(slot);
++ 	econtext->ecxt_scantuple = NULL;
++ 	state->cache=NULL;
++ }
++ 
++ 
++ /* set _level_ column to proper value 
++  * first decompose tuple. if we're found _level_ column,
++  * set it to proper value. them form new tuple.
++  * in tape case free original tuple.
++  */
++ void *
++ setlevel(Tupleconnstate *state,int level, void *tuple,bool free_it, void *prev_tuple){
++ #define REALLOCATTRS 64
++ 	Datum valuesArray[REALLOCATTRS];
++ 	char nullsArray[REALLOCATTRS];
++ 	Datum pvaluesArray[REALLOCATTRS];
++ 	char pnullsArray[REALLOCATTRS];
++ 	Datum *values,*pvalues;
++ 	char *nulls,*pnulls;
++ 	HeapTuple newtup,tup = (HeapTuple)tuple,ptup = (HeapTuple)prev_tuple;
++ 	TupleDesc tdesc = state->tupDesc;
++ 	int natts,i,c;
++   List *tl;
++  	bool		isnull;
++ 
++ 	if(!tuple) return NULL;
++ 	natts = tdesc->natts;
++ 	/* prepare arrays */
++ 	if(natts>REALLOCATTRS){
++ 		values = palloc(natts * sizeof(Datum));
++ 		nulls = palloc(natts * sizeof(char));
++     if(prev_tuple){
++   		pvalues = palloc(natts * sizeof(Datum));
++ 	  	pnulls = palloc(natts * sizeof(char));
++     }else{
++   		pvalues = NULL;
++ 	  	pnulls = NULL;
++     }
++ 	}else{
++ 		values = valuesArray;
++ 		nulls = nullsArray;
++ 		pvalues = pvaluesArray;
++ 		pnulls = pnullsArray;
++ 	}
++ 	/* decompose tuple and substitute attr _level_ with real value */
++ 	for (i = 0; i < natts; i++){
++ 		if(i != state->level){
++ 			values[i] = heap_getattr(tup,
++ 								 i + 1,
++ 								 tdesc,
++ 								 &isnull);
++ 			if (isnull)
++ 				nulls[i] = 'n';
++ 			else
++ 				nulls[i] = ' ';
++ 		}else{
++ 			values[state->level]=Int32GetDatum(level);
++ 			nulls[i] = ' ';
++ 		}
++ 	}
++   if(state->priorsList){
++     List *ttl;
++   
++     if(prev_tuple)
++   	for (i = 0; i < natts; i++){
++ 		  if(i != state->level){
++ 			  pvalues[i] = heap_getattr(ptup,
++ 				  			 i + 1,
++ 								 tdesc,
++ 								 &isnull);
++   			if (isnull)
++ 	  			pnulls[i] = 'n';
++ 		  	else
++ 			  	pnulls[i] = ' ';
++   		}
++ 	  }
++ 
++     foreach(tl,state->priorsList){
++       ttl=lfirst(tl);
++       i=lfirsti(ttl);
++       c=lfirsti(lnext(ttl));
++       if(prev_tuple){
++         nulls[i] = pnulls[c];
++         values[i] = pvalues[c];
++       }else{
++         nulls[i] = 'n';
++       }
++ 		}
++   }
++ 	/* form new */
++ 	tup = heap_formtuple(state->tupDesc,values,nulls);
++ 	if(natts>REALLOCATTRS){
++ 		pfree(values);
++ 		pfree(nulls);
++     if(prev_tuple){
++   		pfree(pvalues);
++ 	  	pfree(pnulls);
++     }
++ 	}
++ 	if(free_it){
++ 		/* make full copy of modified tuple and free original */
++ 		newtup = heap_copytuple(tup);
++ 		tup = newtup;
++ 		FREEMEM(state,((HeapTuple)tuple)->t_len+HEAPTUPLESIZE);
++ 		heap_freetuple(tuple);
++ 		tup = newtup;
++     //also free prev_tuple, if any
++     if(prev_tuple){
++       FREEMEM(state,((HeapTuple)prev_tuple)->t_len+HEAPTUPLESIZE);
++       heap_freetuple(prev_tuple);
++     }
++ 	}
++   return tup;
++ }
++ /*
++  * Fetch the next tuple in forward only direction.
++  * Returns NULL if no more tuples. If should_free is set, the
++  * caller must pfree the returned tuple when done with it.
++  */
++ /* FIXME: add backward direction in future. may be.
++  */
++ void *
++ tupleconn_gettuple(Tupleconnstate *state,
++ 					bool *should_free)
++ {
++ 	void	   *tup=NULL;
++ 	int 		level=0;
++   void    *prev_tuple=NULL;
++ 	  
++ 	if (state->eof_reached || !state->head) return NULL;
++ 	/* check status */
++ 	switch (state->status)
++ 	{
++ 		case TCS_READMEM:
++ 			*should_free = false;
++ 			break;
++ 		case TCS_READFILE:
++ 			*should_free = true;
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_gettuple: invalid state");
++ 			return NULL;		/* keep compiler happy */
++ 	}
++ 
++ 	while(!tup) {
++ 		if(!state->skip_node) {
++ 			if(state->status == TCS_READMEM){
++ 				tup=state->memtuples[state->last->tuple];
++         if(state->last->sup){
++           prev_tuple = state->memtuples[state->last->sup->tuple];
++         }else{
++           prev_tuple = NULL;
++         }
++ 			}else{
++ 				tup=READTUP(state,state->last->tuple);
++         if(state->priorsList && state->last->sup){
++           prev_tuple = READTUP(state,state->last->sup->tuple);
++           // same as a bit below
++           state->out_store[state->last->sup->tuple].tup=NULL;
++         }else{
++           prev_tuple = NULL;
++         }
++ 				/* will be freed in setlevel(), but setlevel()
++ 				 * can't clear tuple's out_store[] cell, so clear it here 
++ 				 */
++ 				state->out_store[state->last->tuple].tup=NULL;
++ 			}
++ 			level=state->last->level;
++ 		}
++ 		if(!state->skip_node && state->last->sub) state->last=state->last->sub;
++ 		else if(state->last->next){
++ 			state->last=state->last->next;
++ 			state->skip_node = false;
++ 		}	else if(state->last->sup){
++ 		  state->last=state->last->sup;
++ 			state->skip_node=true;
++ 		}	else {
++ 		  state->eof_reached = true;
++ 		  break;
++ 		}
++ 	}
++   if(!state->priorsList){
++     prev_tuple = NULL;
++   }
++ 	return setlevel(state,level,tup,*should_free,prev_tuple);
++ }
++ 
++ /*
++  * dumptuples - remove tuples from memory and write to tape
++  */
++ static void
++ dumptuples(Tupleconnstate *state)
++ {
++ 	int			i,j;
++ 	bool	b;
++ 	for (i = 0, j = 0; i < state->tupcount; i++){
++ 		/* don't free pnt list, because we will use it soon, in tupleconn_performconn() */
++ 		if(j < state->pntlast && i == state->pnt[j]->tuple){
++ 			b=false;
++ 			j++;
++ 		}else b=true;
++ 		WRITETUP(state, state->memtuples[i],b,i);
++ 	}
++ }
++ 
++ /*
++  * tupleconn_rescan		- rewind and replay the scan
++  */
++ void
++ tupleconn_rescan(Tupleconnstate *state)
++ {
++ 
++ 	/* check status */
++ 	switch (state->status)
++ 	{
++ 		case TCS_READMEM:
++ 		case TCS_READFILE:
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_rescan: invalid state");
++ 			break;
++ 	}
++ 	state->eof_reached = false;
++ 	state->markpos_eof = false;
++ 	
++ 	state->last = state->head;
++ }
++ 
++ /*
++  * tupleconn_markpos	- saves current position in the tuple sequence
++  */
++ void
++ tupleconn_markpos(Tupleconnstate *state)
++ {
++ 
++ 	/* check status */
++ 	switch (state->status)
++ 	{
++ 		case TCS_READMEM:
++ 		case TCS_READFILE:
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_markpos: invalid state");
++ 			break;
++ 	}
++ 	state->markpos_eof = state->eof_reached;
++ 
++ 	/* file/memtuples positions can be retrieved by state->last
++ 	 * so don't save them
++ 	 */
++ 	state->markpos_last = state->last;
++ 	state->markpos_skip = state->skip_node;
++ }
++ 
++ /*
++  * tupleconn_restorepos - restores current position in connection tree to
++  *						  last saved position
++  */
++ void
++ tupleconn_restorepos(Tupleconnstate *state)
++ {
++ 
++ 	/* check status */
++ 	switch (state->status)
++ 	{
++ 		case TCS_READMEM:
++ 		case TCS_READFILE:
++ 			break;
++ 		default:
++ 			elog(ERROR, "tupleconn_restorepos: invalid state");
++ 			break;
++ 	}
++ 	state->eof_reached = state->markpos_eof;
++ 
++ 	state->last = state->markpos_last;
++ 	state->skip_node = state->markpos_skip;
++ }
++ 
++ /*
++  * Routines specialized for HeapTuple case
++  */
++ 
++ static void *
++ copytup_heap(Tupleconnstate *state, void *tup)
++ {
++ 	HeapTuple	tuple = (HeapTuple) tup;
++ 
++ 	USEMEM(state, HEAPTUPLESIZE + tuple->t_len);
++ 	return (void *) heap_copytuple(tuple);
++ }
++ 
++ /*
++  * tree building procedures
++  */
++  
++ /* add new node next to given, set and return it */
++ ConnectTree *
++ add_next(Tupleconnstate *state,ConnectTree *tr,int tup,int level){
++   ConnectTree *t;
++   MemoryContext morig;
++   
++ 	morig=MemoryContextSwitchTo(state->tree_ctx);
++   t=palloc(sizeof(ConnectTree));
++ 	MemoryContextSwitchTo(morig);
++   memset(t,0,sizeof(ConnectTree));
++   t->tuple=tup;
++   t->level=level;
++   if(tr){
++     tr->next=t;
++     t->prev=tr;
++   }
++   return t;
++ }
++ 
++ /* add new node as child to given, set and return it */
++ ConnectTree *
++ add_sub(Tupleconnstate *state,ConnectTree *tr,int tup,int level){
++   ConnectTree *t,*t1;
++   MemoryContext morig;
++   
++ 	morig=MemoryContextSwitchTo(state->tree_ctx);
++   t=palloc(sizeof(ConnectTree));
++ 	MemoryContextSwitchTo(morig);
++   memset(t,0,sizeof(ConnectTree));
++   t->tuple=tup;
++   t->level=level;
++   if(!tr->sub){
++     tr->sub=t;  
++     t->sup=tr;
++   }else{
++     for(t1=tr->sub;t1->next;t1=t1->next);
++     t1->next=t;
++     t->prev=t1;
++     t->sup=tr;
++   }
++   return t;
++ }
++ 
++ 
++ /*
++  * File storage procedures
++  * this part taken from tuplestore.c, with addition for 
++  * maintaining out_store[].
++  * because of all on-tape tuple placement info is stored in 
++  * out_store[], don't bother to write it on a tape, only
++  * tuple body is stored.
++  */
++  
++ /*
++  * We don't bother to write the HeapTupleData part of the tuple.
++  */
++ 
++ static void
++ writetup_heap(Tupleconnstate *state, void *tup, bool free, int idx)
++ {
++ 	HeapTuple	tuple = (HeapTuple) tup;
++ 
++ 	/* fill placement info */
++ 	state->out_store[idx].len = tuple->t_len;
++ 	BufFileTell(state->myfile,
++ 				&state->out_store[idx].fileno,
++ 				&state->out_store[idx].offs);
++ 	
++ 	if (BufFileWrite(state->myfile, (void *) tuple->t_data,
++ 					 tuple->t_len) != (size_t) tuple->t_len)
++ 		elog(ERROR, "tupleconn: write failed");
++ 	
++ 	/* explanation in dumptuples */
++ 	if(free){
++ //		FREEMEM(state, HEAPTUPLESIZE + tuple->t_len);
++ 		heap_freetuple(tuple);
++ 		state->out_store[idx].tup = NULL;		
++ 	}else{
++ 		state->out_store[idx].tup = (HeapTuple)tup;
++ 	}
++ }
++ 
++ static void *
++ readtup_heap(Tupleconnstate *state, int idx)
++ {
++ 	unsigned int tuplen = state->out_store[idx].len + HEAPTUPLESIZE;
++ 	/* add block readings */
++ 	HeapTuple	tuple = (HeapTuple) palloc(tuplen);
++ 
++ 	USEMEM(state, tuplen);
++ 	/* reconstruct the HeapTupleData portion */
++ 	tuple->t_len = state->out_store[idx].len ;
++ 	ItemPointerSetInvalid(&(tuple->t_self));
++ 	tuple->t_datamcxt = CurrentMemoryContext;
++ 	tuple->t_data = (HeapTupleHeader) (((char *) tuple) + HEAPTUPLESIZE);
++ 	/* seek to the tuple */
++ 	if(BufFileSeek(state->myfile,
++ 			state->out_store[idx].fileno,
++ 			state->out_store[idx].offs,SEEK_SET)!=0)
++ 		elog(ERROR,"tupleconn: can't seek in readtup_heap");
++ 
++ 	/* read in the tuple proper */
++ 	if (BufFileRead(state->myfile, (void *) tuple->t_data,
++ 					tuple->t_len) != (size_t) tuple->t_len)
++ 		elog(ERROR, "tupleconn: unexpected end of data");
++ 
++ 	state->out_store[idx].tup = tuple;
++ 	return (void *) tuple;
++ }
++ 
++ /*
++  * Select comparation function
++  * made from SelectSortFunction() in nodeSort.c.
++  * differences is the name:) and strategy of operator
++  * being searched (here is BTEqualStrategyNumber)
++  */
++  
++ void
++ SelectConnFunction(Oid connOperator,
++ 				   RegProcedure *connFunction,
++ 				   SortFunctionKind *kind)
++ {
++ 	Relation	relation;
++ 	HeapScanDesc scan;
++ 	ScanKeyData skey[1];
++ 	HeapTuple	tuple;
++ 	Form_pg_operator optup;
++ 	Oid			opclass = InvalidOid;
++ 
++ 	ScanKeyEntryInitialize(&skey[0], 0x0,
++ 						   Anum_pg_amop_amopopr,
++ 						   F_OIDEQ,
++ 						   ObjectIdGetDatum(connOperator));
++ 
++ 	relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
++ 	scan = heap_beginscan(relation, SnapshotNow, 1, skey);
++ 
++ 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
++ 	{
++ 		Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
++ 
++ 		if (!opclass_is_btree(aform->amopclaid))
++ 			continue;
++ 		if (aform->amopstrategy == BTEqualStrategyNumber)
++ 		{
++ 			opclass = aform->amopclaid;
++ 			*kind = SORTFUNC_CMP;
++ 			break;				/* done looking */
++ 		}
++ 	}
++ 
++ 	heap_endscan(scan);
++ 	heap_close(relation, AccessShareLock);
++ 
++ 	if (OidIsValid(opclass))
++ 	{
++ 		/* Found a suitable opclass, get its comparator support function */
++ 		tuple = SearchSysCache(AMPROCNUM,
++ 							   ObjectIdGetDatum(opclass),
++ 							   Int16GetDatum(BTORDER_PROC),
++ 							   0, 0);
++ 		if (HeapTupleIsValid(tuple))
++ 		{
++ 			Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
++ 
++ 			*connFunction = aform->amproc;
++ 			ReleaseSysCache(tuple);
++ 			Assert(RegProcedureIsValid(*connFunction));
++ 			return;
++ 		}
++ 	}
++ 
++ 	/*
++ 	 * Can't find a comparator, so use the operator as-is.  Decide whether
++ 	 * it is forward or reverse conn by looking at its name (grotty, but
++ 	 * this only matters for deciding which end NULLs should get conned
++ 	 * to).
++ 	 */
++ 	tuple = SearchSysCache(OPEROID,
++ 						   ObjectIdGetDatum(connOperator),
++ 						   0, 0, 0);
++ 	if (!HeapTupleIsValid(tuple))
++ 		elog(ERROR, "SelectConnFunction: cache lookup failed for operator %u",
++ 			 connOperator);
++ 	optup = (Form_pg_operator) GETSTRUCT(tuple);
++ 	if (strcmp(NameStr(optup->oprname), ">") == 0)
++ 		*kind = SORTFUNC_REVLT;
++ 	else
++ 		*kind = SORTFUNC_LT;
++ 	*connFunction = optup->oprcode;
++ 	ReleaseSysCache(tuple);
++ 
++ 	Assert(RegProcedureIsValid(*connFunction));
++ }
++ 
++ /* ----------------------------------------------------------------
++  *		hashFunc
++  *
++  *  taken from executor/nodeHash.c, it's simplier to copy it here,
++  *  than link to nodeHash.c
++  * ----------------------------------------------------------------
++  */
++ static uint32
++ hashFunc(Datum key, int typLen, bool byVal)
++ {
++ 	unsigned char *k;
++ 
++ 	if (byVal)
++ 	{
++ 		/*
++ 		 * If it's a by-value data type, just hash the whole Datum value.
++ 		 * This assumes that datatypes narrower than Datum are
++ 		 * consistently padded (either zero-extended or sign-extended, but
++ 		 * not random bits) to fill Datum; see the XXXGetDatum macros in
++ 		 * postgres.h. NOTE: it would not work to do hash_any(&key, len)
++ 		 * since this would get the wrong bytes on a big-endian machine.
++ 		 */
++ 		k = (unsigned char *) &key;
++ 		typLen = sizeof(Datum);
++ 	}
++ 	else
++ 	{
++ 		if (typLen > 0)
++ 		{
++ 			/* fixed-width pass-by-reference type */
++ 			k = (unsigned char *) DatumGetPointer(key);
++ 		}
++ 		else if (typLen == -1)
++ 		{
++ 			/*
++ 			 * It's a varlena type, so 'key' points to a "struct varlena".
++ 			 * NOTE: VARSIZE returns the "real" data length plus the
++ 			 * sizeof the "vl_len" attribute of varlena (the length
++ 			 * information). 'key' points to the beginning of the varlena
++ 			 * struct, so we have to use "VARDATA" to find the beginning
++ 			 * of the "real" data.	Also, we have to be careful to detoast
++ 			 * the datum if it's toasted.  (We don't worry about freeing
++ 			 * the detoasted copy; that happens for free when the
++ 			 * per-tuple memory context is reset in ExecHashGetBucket.)
++ 			 */
++ 			struct varlena *vkey = PG_DETOAST_DATUM(key);
++ 
++ 			typLen = VARSIZE(vkey) - VARHDRSZ;
++ 			k = (unsigned char *) VARDATA(vkey);
++ 		}
++ 		else if (typLen == -2)
++ 		{
++ 			/* It's a null-terminated C string */
++ 			typLen = strlen(DatumGetCString(key)) + 1;
++ 			k = (unsigned char *) DatumGetPointer(key);
++ 		}
++ 		else
++ 		{
++ 			elog(ERROR, "hashFunc: Invalid typLen %d", typLen);
++ 			k = NULL;			/* keep compiler quiet */
++ 		}
++ 	}
++ 
++ 	return DatumGetUInt32(hash_any(k, typLen));
++ }
+diff -Prdc --exclude-from=exclude src/include/executor/nodeConn.h src/include/executor/nodeConn.h
+*** src/include/executor/nodeConn.h	Thu Jan  1 00:00:00 1970
+--- src/include/executor/nodeConn.h	Mon Jul  5 08:19:37 2004
+***************
+*** 0 ****
+--- 1,27 ----
++ /*-------------------------------------------------------------------------
++  *
++  * nodeSort.h
++  *
++  *
++  *
++  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
++  * Portions Copyright (c) 1994, Regents of the University of California
++  *
++  * $Id: nodeSort.h,v 1.14 2001/11/05 17:46:33 momjian Exp $
++  *
++  *-------------------------------------------------------------------------
++  */
++ #ifndef NODECONN_H
++ #define NODECONN_H
++ 
++ #include "nodes/plannodes.h"
++ 
++ extern TupleTableSlot *ExecConn(ConnectState *node);
++ extern ConnectState * ExecInitConn(Conn *node, EState *estate);
++ extern int	ExecCountSlotsConn(Conn *node);
++ extern void ExecEndConn(ConnectState *node);
++ extern void ExecConnMarkPos(ConnectState *node);
++ extern void ExecConnRestrPos(ConnectState *node);
++ extern void ExecReScanConn(ConnectState *node, ExprContext *exprCtxt);
++ 
++ #endif   /* NODECONN_H */
+diff -Prdc --exclude-from=exclude src/include/nodes/execnodes.h src/include/nodes/execnodes.h
+*** src/include/nodes/execnodes.h	Thu Jan 22 02:23:35 2004
+--- src/include/nodes/execnodes.h	Mon Jul  5 08:19:37 2004
+***************
+*** 1038,1043 ****
+--- 1038,1062 ----
+  } AggState;
+  
+  /* ----------------
++  *	 ConnectState information
++  *
++  *		conn_Done		indicates whether connection has been performed yet
++  *		tupleconnstate	private state of tuplesort.c
++  * ----------------
++  */
++ typedef struct ConnectState
++ {
++  	ScanState ss;	/* its first field is NodeTag */
++  	bool		conn_Done;
++   
++   ExprState 	*startQual;   /* qual conditions for heads */
++ 	ExprState 	*parentExpr;
++ 	ExprState 	*childExpr;
++ 
++  	void	   *tupleconnstate;
++ } ConnectState;
++  
++ /* ----------------
+   *	 UniqueState information
+   *
+   *		Unique nodes are used "on top of" sort nodes to discard
+diff -Prdc --exclude-from=exclude src/include/nodes/nodes.h src/include/nodes/nodes.h
+*** src/include/nodes/nodes.h	Sun Aug 17 19:58:06 2003
+--- src/include/nodes/nodes.h	Mon Jul  5 08:19:37 2004
+***************
+*** 56,61 ****
+--- 56,62 ----
+  	T_HashJoin,
+  	T_Material,
+  	T_Sort,
++   T_Conn,
+  	T_Group,
+  	T_Agg,
+  	T_Unique,
+***************
+*** 83,88 ****
+--- 84,90 ----
+  	T_HashJoinState,
+  	T_MaterialState,
+  	T_SortState,
++ 	T_ConnectState,
+  	T_GroupState,
+  	T_AggState,
+  	T_UniqueState,
+***************
+*** 98,103 ****
+--- 100,107 ----
+  	T_RangeVar,
+  	T_Expr,
+  	T_Var,
++ 	T_FakeVar,
++ 	T_Prior,
+  	T_Const,
+  	T_Param,
+  	T_Aggref,
+***************
+*** 283,288 ****
+--- 287,293 ----
+  	T_CreateOpClassItem,
+  	T_CompositeTypeStmt,
+  	T_InhRelation,
++   T_HierClause,
+  
+  	/*
+  	 * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
+diff -Prdc --exclude-from=exclude src/include/nodes/parsenodes.h src/include/nodes/parsenodes.h
+*** src/include/nodes/parsenodes.h	Wed Sep 17 04:25:29 2003
+--- src/include/nodes/parsenodes.h	Mon Jul  5 08:19:37 2004
+***************
+*** 82,87 ****
+--- 82,88 ----
+  	Node	   *setOperations;	/* set-operation tree if this is top level
+  								 * of a UNION/INTERSECT/EXCEPT query */
+  
++  	Node     *hierClause; /* CONNECT BY/START WITH clause */
+  	/*
+  	 * If the resultRelation turns out to be the parent of an inheritance
+  	 * tree, the planner will add all the child tables to the rtable and
+***************
+*** 274,279 ****
+--- 275,281 ----
+  	char	   *name;			/* column name or NULL */
+  	List	   *indirection;	/* subscripts for destination column, or
+  								 * NIL */
++ 	int 	prior;
+  	Node	   *val;			/* the value expression to compute or
+  								 * assign */
+  } ResTarget;
+***************
+*** 526,532 ****
+   */
+  typedef SortClause GroupClause;
+  
+! 
+  /*****************************************************************************
+   *		Optimizable Statements
+   *****************************************************************************/
+--- 528,554 ----
+   */
+  typedef SortClause GroupClause;
+  
+! /*
+!  * HierClause -
+!  *	   representation of CONNECT BY/START WITH clauses
+!  * parsed:
+!  * CONNECT BY ( PRIOR expr ) ( op ) ( expr )
+!  * first () is parentExpr
+!  * second () is connOpName
+!  * third () is childExpr
+!  */
+! typedef struct HierClause
+! {
+!  	NodeTag type;
+! 	Node	*parentExpr,*childExpr;
+! 	Node	*connOpName;
+!  	Node	*startQual;		/* Quals for heads */
+!  	Oid	connOp;	/* operator for comparing parent & child exprs*/
+! 	List	*priorsList;
+! 	int	priorsListNItems; 
+! 	int	levelColNum; /* index of _level_ column in target list */
+! 	bool	useHash; /* if true performconn will use hash algorithm */
+! } HierClause;
+  /*****************************************************************************
+   *		Optimizable Statements
+   *****************************************************************************/
+***************
+*** 614,619 ****
+--- 636,642 ----
+  	Node	   *whereClause;	/* WHERE qualification */
+  	List	   *groupClause;	/* GROUP BY clauses */
+  	Node	   *havingClause;	/* HAVING conditional-expression */
++  	List		 *hierClause;    /* CONNECT BY , START WITH clauses*/
+  
+  	/*
+  	 * These fields are used in both "leaf" SelectStmts and upper-level
+diff -Prdc --exclude-from=exclude src/include/nodes/plannodes.h src/include/nodes/plannodes.h
+*** src/include/nodes/plannodes.h	Fri Aug  8 21:42:48 2003
+--- src/include/nodes/plannodes.h	Mon Jul  5 08:19:37 2004
+***************
+*** 283,288 ****
+--- 283,304 ----
+  	Oid		   *sortOperators;	/* OIDs of operators to sort them by */
+  } Sort;
+  
++ /* ----------------
++  *		conn node
++  * ----------------
++  */
++ typedef struct Conn
++ {
++  	Plan		plan;
++  	Node	*startQual;   /* qual conditions for heads */
++ 	Node	*parentExpr;
++ 	Node	*childExpr;
++  	Oid	connOp;
++ 	List	*priorsList;
++ 	int	levelColNum;
++ 	bool	useHash;
++ } Conn;
++ 
+  /* ---------------
+   *	 group node -
+   *		Used for queries with GROUP BY (but no aggregates) specified.
+diff -Prdc --exclude-from=exclude src/include/nodes/primnodes.h src/include/nodes/primnodes.h
+*** src/include/nodes/primnodes.h	Sun Aug 17 23:43:26 2003
+--- src/include/nodes/primnodes.h	Mon Jul  5 08:19:37 2004
+***************
+*** 189,194 ****
+--- 189,226 ----
+  	AttrNumber	varoattno;		/* original value of varattno */
+  } Var;
+  
++ /* FakeVar is same as Var, with several exeptions:
++  *
++  * it's not fetched from the tables.
++  * it's not passed to scan nodes so them don't see it.
++  *		 when we comes to moment when we must fetch value from nowhere 
++  * 		(because of nonexistence of FakeVar as relation attr), we just return
++  * 		0 as Datum. this made at executor/execQual.c:ExecEvalFakeVar().
++  * FakeVar exist only for checking attrs in scan tuple with baseing on targetlist,
++  * 		not in outer or inner relations. so there is no varno.
++  * it never rewritten because of it's based on targetlist where it's present.
++  * 		so there is no *old fields.
++  * now supproted only INT4 type FakeVars (see at executor/execQual.c:ExecEvalFakeVar()).
++  * 		but may be extended.
++  */
++ typedef struct FakeVar
++ {
++ 	NodeTag		type;
++ 	AttrNumber	varattno;		/* attribute number of this var, or zero
++ 								 * for all */
++ 	Oid			vartype;		/* pg_type tuple OID for the type of this
++ 								 * var */
++ 	int32		vartypmod;		/* pg_attribute typmod value */
++ 
++ } FakeVar;
++ 
++ typedef struct Prior
++ {
++ 	NodeTag type;
++ 	int numcol;
++ 	Node *val;
++ } Prior;
++ 
+  /*
+   * Const
+   */
+***************
+*** 727,732 ****
+--- 759,765 ----
+  	Expr		xpr;
+  	Resdom	   *resdom;			/* descriptor for targetlist item */
+  	Expr	   *expr;			/* expression to evaluate */
++ 	int 	prior;
+  } TargetEntry;
+  
+  
+diff -Prdc --exclude-from=exclude src/include/optimizer/planmain.h src/include/optimizer/planmain.h
+*** src/include/optimizer/planmain.h	Fri Aug  8 21:42:50 2003
+--- src/include/optimizer/planmain.h	Mon Jul  5 08:19:37 2004
+***************
+*** 51,56 ****
+--- 51,57 ----
+  extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
+  		   List *distinctList, AttrNumber flagColIdx);
+  extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
++ extern Conn *make_conn(Query *root, List *tlist, Plan *lefttree);
+  
+  /*
+   * prototypes for plan/initsplan.c
+diff -Prdc --exclude-from=exclude src/include/optimizer/planner.h src/include/optimizer/planner.h
+*** src/include/optimizer/planner.h	Mon Aug  4 02:40:14 2003
+--- src/include/optimizer/planner.h	Mon Jul  5 08:19:37 2004
+***************
+*** 21,24 ****
+--- 21,27 ----
+  extern Plan *planner(Query *parse, bool isCursor, int cursorOptions);
+  extern Plan *subquery_planner(Query *parse, double tuple_fraction);
+  
++ extern Plan *make_connplan(Query *parse, List *tlist,
++                         Plan *plannode);
++ 
+  #endif   /* PLANNER_H */
+diff -Prdc --exclude-from=exclude src/include/parser/parse_clause.h src/include/parser/parse_clause.h
+*** src/include/parser/parse_clause.h	Sun Aug 17 19:58:06 2003
+--- src/include/parser/parse_clause.h	Mon Jul  5 08:19:37 2004
+***************
+*** 31,36 ****
+--- 31,37 ----
+  					List *targetlist, bool resolveUnknown);
+  extern List *transformDistinctClause(ParseState *pstate, List *distinctlist,
+  						List *targetlist, List **sortClause);
++ extern Node *transformHierClause(ParseState *pstate, Query *qry, List *hier);
+  
+  extern List *addAllTargetsToSortList(ParseState *pstate,
+  						List *sortlist, List *targetlist,
+diff -Prdc --exclude-from=exclude src/include/parser/parse_node.h src/include/parser/parse_node.h
+*** src/include/parser/parse_node.h	Mon Aug  4 02:40:14 2003
+--- src/include/parser/parse_node.h	Mon Jul  5 08:19:37 2004
+***************
+*** 61,66 ****
+--- 61,80 ----
+  	bool		p_hasSubLinks;
+  	bool		p_is_insert;
+  	bool		p_is_update;
++ 
++ 	AttrNumber p_hierLevelColIdx; /* _level_ column number for hier clause, if any. 
++ 										* set in transformHierClause() */
++ 	bool    p_hierExists;
++ 	bool		p_addTle; /* flag used in parse_expr.c:transformIndirection().
++ 										 * if set all vars going thought will be checked against
++ 										 * current targetlist (field below), and if not present,
++ 										 * added to it as junk.
++ 										 * this is needed in HierClause case. in parent/child Expr and startQual
++ 										 * may be preset attributes which is not in targetlist given by user, so
++ 										 * at transformation stage we'll add them.
++ 										 */
++ 	List		 *p_curTlist; /* current target list (see above)*/
++ 
+  	Relation	p_target_relation;
+  	RangeTblEntry *p_target_rangetblentry;
+  } ParseState;
+diff -Prdc --exclude-from=exclude src/include/stamp-h src/include/stamp-h
+*** src/include/stamp-h	Thu Jan  1 00:00:00 1970
+--- src/include/stamp-h	Thu Jul  8 16:28:04 2004
+***************
+*** 0 ****
+--- 1 ----
++ 
+diff -Prdc --exclude-from=exclude src/include/utils/tupleconn.h src/include/utils/tupleconn.h
+*** src/include/utils/tupleconn.h	Thu Jan  1 00:00:00 1970
+--- src/include/utils/tupleconn.h	Mon Jul  5 08:19:37 2004
+***************
+*** 0 ****
+--- 1,65 ----
++ /*-------------------------------------------------------------------------
++  *
++  * tuplestore.h
++  *	  Generalized routines for temporary tuple storage.
++  *
++  * This module handles temporary storage of tuples for purposes such
++  * as Materialize nodes, hashjoin batch files, etc.  It is essentially
++  * a dumbed-down version of tuplesort.c; it does no sorting of tuples
++  * but can only store a sequence of tuples and regurgitate it later.
++  * A temporary file is used to handle the data if it exceeds the
++  * space limit specified by the caller.
++  *
++  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
++  * Portions Copyright (c) 1994, Regents of the University of California
++  *
++  * $Id: tuplestore.h,v 1.6 2001/11/05 17:46:36 momjian Exp $
++  *
++  *-------------------------------------------------------------------------
++  */
++ #ifndef TUPLECONN_H
++ #define TUPLECONN_H
++ 
++ #include "access/htup.h"
++ #include "nodes/nodes.h"
++ #include "executor/executor.h"
++ 
++ /* Tuplestorestate is an opaque type whose details are not known outside
++  * tuplestore.c.
++  */
++ typedef struct Tupleconnstate Tupleconnstate;
++ 
++ /*
++  * Currently we only need to store HeapTuples, but it would be easy
++  * to support the same behavior for IndexTuples and/or bare Datums.
++  */
++ 
++ extern Tupleconnstate *tupleconn_begin_heap(
++ 					  int maxKBytes, TupleDesc tupDesc, Oid connOp, List *priorsList,
++             int levelColNum,double rows,bool useHash);
++ 
++ extern void tupleconn_puttuple(Tupleconnstate *state, void *tuple,int head);
++ 
++ extern void tupleconn_donestoring(Tupleconnstate *state);
++ 
++ extern void *tupleconn_gettuple(Tupleconnstate *state,
++ 					bool *should_free);
++ 
++ #define tupleconn_getheaptuple(state, should_free) \
++ 	((HeapTuple) tupleconn_gettuple(state, should_free))
++ 
++ extern void tupleconn_end(Tupleconnstate *state);
++ 
++ extern void tupleconn_performconn(Tupleconnstate *state, Node *parentExpr, Node *childExpr, ExprContext *econtext);
++ 
++ /*
++  * These routines may only be called if randomAccess was specified 'true'.
++  * Likewise, backwards scan in gettuple/getdatum is only allowed if
++  * randomAccess was specified.
++  */
++ 
++ extern void tupleconn_rescan(Tupleconnstate *state);
++ extern void tupleconn_markpos(Tupleconnstate *state);
++ extern void tupleconn_restorepos(Tupleconnstate *state);
++ 
++ #endif   /* TUPLECONN_H */
--- patch-postgres74-server ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200508092139.j79LdMQH001774>