From owner-freebsd-ports-bugs@FreeBSD.ORG Tue Aug 9 20:50:23 2005 Return-Path: X-Original-To: freebsd-ports-bugs@hub.freebsd.org Delivered-To: freebsd-ports-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 2F1EE16A440 for ; Tue, 9 Aug 2005 20:50:23 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 3964543D5F for ; Tue, 9 Aug 2005 20:50:22 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.3/8.13.3) with ESMTP id j79KoM0v083918 for ; Tue, 9 Aug 2005 20:50:22 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.3/8.13.1/Submit) id j79KoMHu083917; Tue, 9 Aug 2005 20:50:22 GMT (envelope-from gnats) Resent-Date: Tue, 9 Aug 2005 20:50:22 GMT Resent-Message-Id: <200508092050.j79KoMHu083917@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-ports-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Marcos Tischer Vallim Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 8883816A425 for ; Tue, 9 Aug 2005 20:45:08 +0000 (GMT) (envelope-from mtv@widesoft.com.br) Received: from smtp-gw.widesoft.com.br (carbono.widesoft.com.br [200.246.206.6]) by mx1.FreeBSD.org (Postfix) with ESMTP id BF1D943D70 for ; Tue, 9 Aug 2005 20:43:55 +0000 (GMT) (envelope-from mtv@widesoft.com.br) Received: from loadbalancer.widesoft.com.br (unknown [172.26.200.130]) by smtp-gw.widesoft.com.br (Postfix) with ESMTP id 1D6A643325 for ; Tue, 9 Aug 2005 17:37:18 -0300 (BRT) Received: from loadbalancer.widesoft.com.br (localhost.widesoft.com.br [127.0.0.1]) by loadbalancer.widesoft.com.br (8.13.3/8.13.3) with ESMTP id j79LdMIx001775 for ; Tue, 9 Aug 2005 18:39:22 -0300 (BRT) (envelope-from mtv@loadbalancer.widesoft.com.br) Received: (from root@localhost) by loadbalancer.widesoft.com.br (8.13.3/8.13.3/Submit) id j79LdMQH001774; Tue, 9 Aug 2005 18:39:22 -0300 (BRT) (envelope-from mtv) Message-Id: <200508092139.j79LdMQH001774@loadbalancer.widesoft.com.br> Date: Tue, 9 Aug 2005 18:39:22 -0300 (BRT) From: Marcos Tischer Vallim To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Cc: Subject: ports/84718: [PATCH] databases/postgresql74-server: Add option from query hierarchy "CONNECT BY" X-BeenThere: freebsd-ports-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Marcos Tischer Vallim List-Id: Ports bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 09 Aug 2005 20:50:23 -0000 >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;ipriorsListNItems;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;ihavingQual){ ++ 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 constraints_set_list + %type constraints_set_mode + ++ %type startwith_clause ++ %type 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 ++ #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;itupcount;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;itupcount;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;jpntlast && !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;itupcount;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;jpntlast;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: